Domain Model, validation, and pushing errors to the model

Looking into DDD and something I noticed is that business logic should be in the model, otherwise you just have property bags. That said how do you handle pieces of validation that require a trip to the database?

For example, you have an domain object that represent categories for a blogging engine.

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

From a developer’s perspective, you could create an empty category, but this isn’t valid in the business world. A Category requires a Name to be a valid category, therefore we end up with:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Category(string name)
    {
        Name = name;
    }
}

Now, a Category can’t be created without a name. But, we can’t have duplicate Category names either, that would just cause confusing and redundancy. I don’t want the entity to handle it themselves because this would end up more of an Active Record Pattern and I don’t want the model to be “self-aware/persisting” so to speak.

So to this point, where should this validation take place. It is important that if the user types an duplicate Category, we would notify the user of this. Should there be a “service” layer that takes a UI model and turns it into the domain model and attempts to validate it, pushing errors back to the UI, or should there be something closer to the data layer that handles this?

Someone please set me straight because I really like where DDD is going, I just can’t make all the connections to get there myself.

5

I would create a domain service responsible for validating the uniqueness of the category. In my solution domain defines the interface of the service, infrastructural part implements it using a database.

Please also take a look at https://stackoverflow.com/questions/516615/validation-in-a-domain-driven-design.

1

business logic should be in the model, otherwise you just have property bags

What is business logic?
Business logic is, that your object knows it’s state and could answer questions like order.IsDistributed or order.IsQualityAssuranceDone or the like.
It is no business logic, if you ask the question is this order already in the database. That is a different responsibility: It belongs not to the order itself, since it isn’t a state of the order, it is the state of the database. Therefore you should ask the database for that answer.

From a developer’s perspective, you could create an empty category, but this isn’t valid in the business world

To maintain a clean state, you should use properties (in C#) / getter and setter, where you implement validation-logic, to keep the object’s state clean and tidy.

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Category(string name)
    {
        Name = name;
    }
}

This design has a big flaw: It mirrors not your requirement exactly of having a name, since the constructor could be called with null, which is not in every sense a valid name. On the other hand, you could use the builder pattern and do the construction via a builder-interface.

Now, a Category can’t be created without a name. But, we can’t have duplicate Category names either, that would just cause confusing and redundancy.

These are two different concerns. One belongs to the object’s state the other to the database’s state.

Should there be a “service” layer that takes a UI model and turns it into the domain model and attempts to validate it, pushing errors back to the UI, or should there be something closer to the data layer that handles this?

Whether you use a service-layer is up to you. Sometimes it is drowning little puppies to make the indirection. It depends on how much logic is there.

Q:How do we create objects?
A: Using Domain Factories!

Since the creation of a category contains complex logic (we need to validate uniqueness), we will utilize a domain factory to create the object for us.

Inside that factory I’ll use a UniqueCategoryNameValidator service that calls into the data-store (a DB in ur case) and check if the category is unique. Some people might refer to this approach as using the specification pattern as follows:

UniqueCategoryNameSpecification.isSatisfiedBy(category); 

Using a specification makes the check for uniqueness explicit in your domain. The specification would use an infrastructure service to query the database.

What I do is that I build my subjects (subordinates, objects within a domain) in such a way that they can never be instantiated by anyone other than the domain itself. (The main or central domain object.) This is in line with best practices: never use the new operator to instantiate objects that do not belong to you, (objects from other packages/namespaces/assemblies,) always invoke factories to do that. The domain is in a position of using new to instantiate its own subjects, because they belong to it.

Instantiation of subjects only by the domain can be enforced by declaring their constructors as package-private, (assembly-internal,) or, better yet, by declaring the subject classes themselves as package-private, (assembly-internal,) and having them implement public interfaces, (interfaces exposed by the domain,) so that the outside world cannot even think of instantiating them.

So, since subjects can only be instantiated by the domain, it is very easy to have the domain perform the necessary validation when creating them, as another answer suggested, so as to ensure that the subjects are valid right from the start.

However, this does not completely solve the problem, because you may later want to rename a subject, (store a different name in it,) in which case you must again be prevented from giving it a name which is already taken by a different subject. Clearly, access to the database is needed at all times, in order to provide a complete service.

So, the solution that I have found for this is that every subject knows its domain.

The domain is always passed as the first parameter to the constructor of every subject. Every single instantiation of a subject by the domain looks like this: new Subject( this, ... ). In a sense, the role played by the domain in domain-driven programming parallels the role played by the object in object-oriented programming: Just as every method accepts as a hidden first parameter the this pointer, which points to the object, so does every subject accept as its first constructor parameter the domain pointer, which points to the domain.

Thus, each and every subject is perfectly capable of performing whatever validation is necessary, by invoking the domain to do its validation.

If you do not want your subjects to be tied to the domain, then your subjects may be thought of as having an extra state, indicating whether they are at any given moment parts of a domain or not. (persisted or not, equivalent to the “attached/detached” state of Hibernate.) So, an object may begin its life as not part of the domain, (not persisted, detached in Hibernate parlance,) in which case it cannot perform any validation, and later it may become part of the domain, (persisted, attached in Hibernate parlance,) at which point it receives a reference to the domain, and it may do whatever validation is necessary. Later you may even remove it from the domain, (detach it,) at which point you would presumably nullify the domain pointer and the object would again be unable to perform validation.

we can’t have duplicate Category names either, that would just cause confusing and redundancy.

Assuming that you have a business invariant that requires no duplicate category names, then the responsibility for enforcing that invariant belongs in some aggregate. Since enforcing the rule requires having access to all of the category names currently in use, that same aggregate must include, as part of its state, that collection of names.

This is basic aggregate construction – the consistency check and the state go together.

where should this validation take place?

In the reserveName() command of the aggregate.

Should there be a “service” layer that takes a UI model and turns it into the domain model and attempts to validate it?

No, there shouldn’t be anything unusual here. Client wants to use a new name, so it sends a representation of the ReserveName command to the application. The application rehydrates the command, locates the appropriate command handler, and dispatches the command to the handler. The handler loads the aggregate, and invokes the reserveName() method using the arguments supplied. This exactly follows the same pattern as every other command you dispatch to the domain model.

If the aggregate discovers that the category name is already in use, then it throws a domain exception, and your application reports it to the client; again, exactly like every other command routed to the domain model.

Which aggregate is “the aggregate”? Beats me — you need to look in the ubiquitous language to discover the aggregate that enforces this invariant. For instance, there might be an aggregate that has this responsibility and nothing else. In a toy example like this one, you might pretend that CategoryNameReservationBook is something that makes sense to the business. Or perhaps NameReservationBook, with one NRB instance being used for category names, and another NRB instance used for blog post titles, or some such thing.

When you discuss this with your domain experts, you may find that “no duplicate categories” isn’t really a hard constraint. In ddd, it is important to hear the difference between “that must never happen” and “that shouldn’t happen”; the latter phrasing implies that it does happen occasionally, and if so there’s likely to already be a process for that contingency.

You may also find, in the discussion, that this isn’t a business constraint at all; in which cast you get it out of the domain model completely — if it’s something real, maybe the application handles is alone, or maybe there is some other domain model (perhaps in another bounded context) that is responsible for it.

Trying to support this constraint with services and the like doesn’t really work; the service running in this transaction can’t see what’s being changed in other transactions, so you get a data race. You have the same problem trying to use a specification — the data used to initialize the specification is stale, and another transaction can be running here.

Domain models with race conditions are inherently suspicious anyway; if two users working on separate tasks are blocking each other, then something is wrong. Udi Dahan went so far as to write that race conditions don’t exist — that in the business world, the conflict isn’t prevented, so much as it is detected and mitigated.

So check, and double check, and triple check that maybe the uniqueness constraint isn’t all that, as far as the domain experts are concerned. But if it really is a business rule that there be no duplicates, then it follows, unconditionally, that there is an aggregate responsible for keeping track of that.

Upon further consideration, a uniqueness problem like this may be scoped; the programmer’s blog has a category named rest, and the health blog has a category named rest, but they aren’t the same thing at all. This could be supported by giving each blog its own reservation book, but it may also be a hint that Category merely an entity, subordinate to some other aggregate.

Why don’t you create a service holding a collection of all categories? Then you can do validation very easy. An example:

class BlogCategoryService 
{
   private List<Category> _categories = new List<Category>();

   public void AddCategory(string categoryName) 
   {
       if (_categories.Any(c => c.Name == categoryName)) 
           throw new DuplicateCategoryException(categoryName);

       var newId = ...

       _categories.Add(new Category(newId, categoryName));
   }
   ...
}

Do the name validation in your Category class:

public class Category
{
    public int Id { get; private set; }
    public string Name { get; private set; }

    public Category(int id, string name)
    {
        ValidateName(name);

        Id = id;
        Name = name;
    }

    private void ValidateName(string name)
    {
       if (string.IsNullOrWhitespace(categoryName))
           throw new ArgumentException('Category name may not be null or whitespace.');
    }

}

If you want to change name on a category, you could depend on your service:

public class Category
{
    ...

    public void ChangeName(BlogCategoryService service, string newName)
    {
        if (service == null)
            throw new ArgumentNullException('service');

        ValidateName(newName);

        if (!service.IsUniqueCategoryName(newName))
            throw new DuplicateCategoryException(newName);

        Name = newName;
    }
}

public class BlogCategoryService 
{

    ...
    public bool IsUniqueCategoryName(string name)
    {
        return !_categories.Any(c => c.Name == name);
    }
}

Also add a unique constraint on the name column of the blog category table to to guard against someone making changes outside of your BlogCategoryService.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Domain Model, validation, and pushing errors to the model

Looking into DDD and something I noticed is that business logic should be in the model, otherwise you just have property bags. That said how do you handle pieces of validation that require a trip to the database?

For example, you have an domain object that represent categories for a blogging engine.

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

From a developer’s perspective, you could create an empty category, but this isn’t valid in the business world. A Category requires a Name to be a valid category, therefore we end up with:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Category(string name)
    {
        Name = name;
    }
}

Now, a Category can’t be created without a name. But, we can’t have duplicate Category names either, that would just cause confusing and redundancy. I don’t want the entity to handle it themselves because this would end up more of an Active Record Pattern and I don’t want the model to be “self-aware/persisting” so to speak.

So to this point, where should this validation take place. It is important that if the user types an duplicate Category, we would notify the user of this. Should there be a “service” layer that takes a UI model and turns it into the domain model and attempts to validate it, pushing errors back to the UI, or should there be something closer to the data layer that handles this?

Someone please set me straight because I really like where DDD is going, I just can’t make all the connections to get there myself.

5

I would create a domain service responsible for validating the uniqueness of the category. In my solution domain defines the interface of the service, infrastructural part implements it using a database.

Please also take a look at https://stackoverflow.com/questions/516615/validation-in-a-domain-driven-design.

1

business logic should be in the model, otherwise you just have property bags

What is business logic?
Business logic is, that your object knows it’s state and could answer questions like order.IsDistributed or order.IsQualityAssuranceDone or the like.
It is no business logic, if you ask the question is this order already in the database. That is a different responsibility: It belongs not to the order itself, since it isn’t a state of the order, it is the state of the database. Therefore you should ask the database for that answer.

From a developer’s perspective, you could create an empty category, but this isn’t valid in the business world

To maintain a clean state, you should use properties (in C#) / getter and setter, where you implement validation-logic, to keep the object’s state clean and tidy.

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Category(string name)
    {
        Name = name;
    }
}

This design has a big flaw: It mirrors not your requirement exactly of having a name, since the constructor could be called with null, which is not in every sense a valid name. On the other hand, you could use the builder pattern and do the construction via a builder-interface.

Now, a Category can’t be created without a name. But, we can’t have duplicate Category names either, that would just cause confusing and redundancy.

These are two different concerns. One belongs to the object’s state the other to the database’s state.

Should there be a “service” layer that takes a UI model and turns it into the domain model and attempts to validate it, pushing errors back to the UI, or should there be something closer to the data layer that handles this?

Whether you use a service-layer is up to you. Sometimes it is drowning little puppies to make the indirection. It depends on how much logic is there.

Q:How do we create objects?
A: Using Domain Factories!

Since the creation of a category contains complex logic (we need to validate uniqueness), we will utilize a domain factory to create the object for us.

Inside that factory I’ll use a UniqueCategoryNameValidator service that calls into the data-store (a DB in ur case) and check if the category is unique. Some people might refer to this approach as using the specification pattern as follows:

UniqueCategoryNameSpecification.isSatisfiedBy(category); 

Using a specification makes the check for uniqueness explicit in your domain. The specification would use an infrastructure service to query the database.

What I do is that I build my subjects (subordinates, objects within a domain) in such a way that they can never be instantiated by anyone other than the domain itself. (The main or central domain object.) This is in line with best practices: never use the new operator to instantiate objects that do not belong to you, (objects from other packages/namespaces/assemblies,) always invoke factories to do that. The domain is in a position of using new to instantiate its own subjects, because they belong to it.

Instantiation of subjects only by the domain can be enforced by declaring their constructors as package-private, (assembly-internal,) or, better yet, by declaring the subject classes themselves as package-private, (assembly-internal,) and having them implement public interfaces, (interfaces exposed by the domain,) so that the outside world cannot even think of instantiating them.

So, since subjects can only be instantiated by the domain, it is very easy to have the domain perform the necessary validation when creating them, as another answer suggested, so as to ensure that the subjects are valid right from the start.

However, this does not completely solve the problem, because you may later want to rename a subject, (store a different name in it,) in which case you must again be prevented from giving it a name which is already taken by a different subject. Clearly, access to the database is needed at all times, in order to provide a complete service.

So, the solution that I have found for this is that every subject knows its domain.

The domain is always passed as the first parameter to the constructor of every subject. Every single instantiation of a subject by the domain looks like this: new Subject( this, ... ). In a sense, the role played by the domain in domain-driven programming parallels the role played by the object in object-oriented programming: Just as every method accepts as a hidden first parameter the this pointer, which points to the object, so does every subject accept as its first constructor parameter the domain pointer, which points to the domain.

Thus, each and every subject is perfectly capable of performing whatever validation is necessary, by invoking the domain to do its validation.

If you do not want your subjects to be tied to the domain, then your subjects may be thought of as having an extra state, indicating whether they are at any given moment parts of a domain or not. (persisted or not, equivalent to the “attached/detached” state of Hibernate.) So, an object may begin its life as not part of the domain, (not persisted, detached in Hibernate parlance,) in which case it cannot perform any validation, and later it may become part of the domain, (persisted, attached in Hibernate parlance,) at which point it receives a reference to the domain, and it may do whatever validation is necessary. Later you may even remove it from the domain, (detach it,) at which point you would presumably nullify the domain pointer and the object would again be unable to perform validation.

we can’t have duplicate Category names either, that would just cause confusing and redundancy.

Assuming that you have a business invariant that requires no duplicate category names, then the responsibility for enforcing that invariant belongs in some aggregate. Since enforcing the rule requires having access to all of the category names currently in use, that same aggregate must include, as part of its state, that collection of names.

This is basic aggregate construction – the consistency check and the state go together.

where should this validation take place?

In the reserveName() command of the aggregate.

Should there be a “service” layer that takes a UI model and turns it into the domain model and attempts to validate it?

No, there shouldn’t be anything unusual here. Client wants to use a new name, so it sends a representation of the ReserveName command to the application. The application rehydrates the command, locates the appropriate command handler, and dispatches the command to the handler. The handler loads the aggregate, and invokes the reserveName() method using the arguments supplied. This exactly follows the same pattern as every other command you dispatch to the domain model.

If the aggregate discovers that the category name is already in use, then it throws a domain exception, and your application reports it to the client; again, exactly like every other command routed to the domain model.

Which aggregate is “the aggregate”? Beats me — you need to look in the ubiquitous language to discover the aggregate that enforces this invariant. For instance, there might be an aggregate that has this responsibility and nothing else. In a toy example like this one, you might pretend that CategoryNameReservationBook is something that makes sense to the business. Or perhaps NameReservationBook, with one NRB instance being used for category names, and another NRB instance used for blog post titles, or some such thing.

When you discuss this with your domain experts, you may find that “no duplicate categories” isn’t really a hard constraint. In ddd, it is important to hear the difference between “that must never happen” and “that shouldn’t happen”; the latter phrasing implies that it does happen occasionally, and if so there’s likely to already be a process for that contingency.

You may also find, in the discussion, that this isn’t a business constraint at all; in which cast you get it out of the domain model completely — if it’s something real, maybe the application handles is alone, or maybe there is some other domain model (perhaps in another bounded context) that is responsible for it.

Trying to support this constraint with services and the like doesn’t really work; the service running in this transaction can’t see what’s being changed in other transactions, so you get a data race. You have the same problem trying to use a specification — the data used to initialize the specification is stale, and another transaction can be running here.

Domain models with race conditions are inherently suspicious anyway; if two users working on separate tasks are blocking each other, then something is wrong. Udi Dahan went so far as to write that race conditions don’t exist — that in the business world, the conflict isn’t prevented, so much as it is detected and mitigated.

So check, and double check, and triple check that maybe the uniqueness constraint isn’t all that, as far as the domain experts are concerned. But if it really is a business rule that there be no duplicates, then it follows, unconditionally, that there is an aggregate responsible for keeping track of that.

Upon further consideration, a uniqueness problem like this may be scoped; the programmer’s blog has a category named rest, and the health blog has a category named rest, but they aren’t the same thing at all. This could be supported by giving each blog its own reservation book, but it may also be a hint that Category merely an entity, subordinate to some other aggregate.

Why don’t you create a service holding a collection of all categories? Then you can do validation very easy. An example:

class BlogCategoryService 
{
   private List<Category> _categories = new List<Category>();

   public void AddCategory(string categoryName) 
   {
       if (_categories.Any(c => c.Name == categoryName)) 
           throw new DuplicateCategoryException(categoryName);

       var newId = ...

       _categories.Add(new Category(newId, categoryName));
   }
   ...
}

Do the name validation in your Category class:

public class Category
{
    public int Id { get; private set; }
    public string Name { get; private set; }

    public Category(int id, string name)
    {
        ValidateName(name);

        Id = id;
        Name = name;
    }

    private void ValidateName(string name)
    {
       if (string.IsNullOrWhitespace(categoryName))
           throw new ArgumentException('Category name may not be null or whitespace.');
    }

}

If you want to change name on a category, you could depend on your service:

public class Category
{
    ...

    public void ChangeName(BlogCategoryService service, string newName)
    {
        if (service == null)
            throw new ArgumentNullException('service');

        ValidateName(newName);

        if (!service.IsUniqueCategoryName(newName))
            throw new DuplicateCategoryException(newName);

        Name = newName;
    }
}

public class BlogCategoryService 
{

    ...
    public bool IsUniqueCategoryName(string name)
    {
        return !_categories.Any(c => c.Name == name);
    }
}

Also add a unique constraint on the name column of the blog category table to to guard against someone making changes outside of your BlogCategoryService.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật