Preface: The code that has been building the project structure before, a bit off our topic (DDD), this article we continue to talk about another important point of knowledge in DDD: Domain Services. About the use of Domain Services, the book is also more obscure, in this regard according to the blogger's own understanding of the use of this knowledge point.
A preliminary study of DDD Domain-driven Design series articles:
- C # Advanced Series--DDD Field Drive Design (i): aggregation
- C # Advanced Series--DDD Field Drive Design (II.): Warehousing repository (UP)
- C # Advanced Series--DDD Field drive Design (iii): Warehousing repository (next)
- C # Advanced Series--DDD field drive Design (iv): WCF Build
- C # Advanced Series--DDD Field Drive Design (V): AutoMapper use
- C # Advanced Series--DDD field drive Design (vi): Domain Services
I. Introduction of Domain Services
In domain-driven design: The core complexity of software is the answer. Eric.eva, the author defines Domain Services in this book: some important domain operations that do not fit into the categories of entities (entity) and Value objects (value object), which are essentially activities or actions, not things, When an important process or conversion operation in a domain is not part of the natural responsibility of an entity or value object, you should add an operation as a standalone interface to the model and declare it as a service.
A service is an operation provided as an interface that is independent in the model, unlike entity and value objects, which emphasize relationships with other objects and define only what can be done for the customer. In general, it is inappropriate for those entities and value objects to be placed in, and it is more biased to the relationship between entities and entities, and in this scenario we need to use domain service. See here, someone is depressed, since the role of Domain Services is to deal with the relationship between entities and entities, then why not put it in the application layer, the role of the application layer is not to coordinate the task? Indeed, in theory, the application layer can be solved, but the blogger's understanding is that one of the design principles of DDD is to try to plump the domain model, advocating a congested domain model, which means that the domain logic associated with the domain model is preferably a methodological domain layer, and it is clear that the relationship between entities and entities is generally domain logic, So it's best to put it on the field floor. Of course, this is not absolute, but the blogger's own understanding.
We would like to give an example, for example, in our rights management, if a function is required, assign the specified user to the defined permissions, such as the interface.
void Assignpower (Tb_users ouser, Tb_role orole)
According to our previous aggregation, should this interface be placed within the user's aggregation or role aggregation? Obviously, the feeling is not appropriate, this interface contains two aggregated entities, so like this situation, you can consider using Domain Services to solve. If you have to say that the method is not designed, it is possible to put it in the application layer. I can only say, hehe, don't make trouble.
Ii. Use of Domain Services
do You remember the Services folder in our domain layer when we introduced warehousing? Below, we will write a domain service function step after step based on the function assigned to the specified user.
1, the modification of the aggregation Division
Here is a question, for bloggers in C # Advanced series--DDD Field Drive Design (a): Aggregation of the division of the aggregation, now want to exist unreasonable place, last said should be divided into 4 aggregations, but did not take into account the user and role is many-to-many relationship, so need to user role table TB _userrole is divided into a single aggregation, so a total of 5 aggregations, which is what is said in the DDD inside the aggregation is a comparison of the experience of the programmer, for the misunderstanding caused by the aggregation division is sorry. I need to make a change in the appropriate place.
The domain entity inherits the aggregation base class:
Public Partial class Tb_userrole:aggregateroot { }
New warehousing interface and warehousing implementation
Public Interface Iuserrolerepository:irepository<tb_userrole> { }
[Export (typeof(iuserrolerepository))] Public class Userrolerepository:efbaserepository<tb_userrole>, iuserrolerepository { }
2, the construction of the domain service code
interface and implementation of service folder building services at the domain level
Public Interface Ipowermanagerdomainservice { void assignpower (tb_users ouser, Tb_role orole); }
[Export (typeof(Ipowermanagerdomainservice))] Public classPowermanagerdomainservice:ipowermanagerdomainservice {PrivateIuserrepository _userrepository =NULL; PrivateIrolerepository _rolerepository =NULL; PrivateIuserrolerepository _userrolerepository =NULL; [Importingconstructor] PublicPowermanagerdomainservice (iuserrolerepository ouserrolerepository) {_userrolerepository=ouserrolerepository; } Public voidAssignpower (tb_users ouser, Tb_role orole) {if(ouser = =NULL|| Orole = =NULL) { return; } varOuserrole = _userrolerepository.find (x = x.user_id = = ouser.user_id && x.role_id = =orole.role_id). FirstOrDefault (); if(Ouserrole = =NULL) {Ouserrole=NewTb_userrole (); ouserrole.role_id=orole.role_id; ouserrole.user_id=ouser.user_id; Ouserrole.id=Guid.NewGuid (). ToString (); _userrolerepository.insert (Ouserrole); } } }
In the implementation class powermanagerdomainservice of the domain service, we used the MEF [Importingconstructor] Import constructor feature, The premise of using this feature is that the parameter type Iuserrolerepository in the constructor must be annotated with export exported, and the implementation class that we saw in the previous iuserrolerepository is marked for export, Therefore, this feature can be used to successfully import the user role of the storage implementation class instance.
3. Call Test
In the application layer we will write the test code:
classProgram {[Import (Allowdefault=false, allowrecomposition=true, requiredcreationpolicy=creationpolicy.any,source=Importsource.any)] PublicIpowermanagerdomainservice Powerdomainservice {Get;Set; }
Static voidMain (string[] args) { varOprogram =NewProgram (); Regisgter.regisgter (). Composeparts (Oprogram); varouser =NewTb_users () {user_id ="04acd48a819447d388b20dffb15f672e" }; varOrole =NewTb_role () {role_id ="CCCC" }; OProgram.powerDomainService.AssignPower (ouser, orole);Console.readkey (); } }
It is important to note that although the implementation class of the domain service Powermanagerdomainservice defines an argument constructor, if we do not apply the method of IOC injection, we need to pass the new object to the storage object, However, due to the use of the import of the constructor function, the parameters are passed through the import of the constructor, so it is not necessary to concern the constructor parameters when using the application layer. This Bo master is also tangled half a day, the background debugging program just know. Isn't that so, let 's take a look at:
Let's take a break in the constructor with the argument to see
The arguments to the constructor are passed in this way. The execution of the method is very simple logic.
4. Summary
(1) MEF for the import of a parameter constructor use the [importingconstructor] attribute, and the parameter type can be imported.
(2) because the domain service interface and implementation are placed in the domain layer inside, and the domain service calls the warehousing implementation logic, so look at the domain layer and the realization of the storage knead together, so that will cause the domain layer of impure? The answer is no, we look at the domain service implementation class Powermanagerdomainservice inside the code, the logic inside is through the storage interface object Iuserrolerepository _userrolerepository to deal with, That is, the domain service does not call the specific warehousing implementation, or warehousing interface in dealing with, warehousing implementation is in operation at the time through the MEF dynamic injection into. So the design can still maintain the purity of the domain layer, do not rely on the specific warehousing implementation.
(3) In the above example, since the only use of iuserrolerepository this storage interface, then this function can be placed directly in the user role of the warehousing implementation without the use of domain Services? At first, bloggers have this kind of doubts, but later think about it or not, because for the rights module, the UI front-end is not and like Dto_tb_userrole object, because it has only user ID and role ID, for the UI has no practical significance. So the UI only passes objects such as users and roles, and the two objects belong to different aggregations, which is best used for domain Services. Of course you can also divide the 3 tb_userrole, Tb_users, tb_role into one aggregation, tb_userrole as the aggregation root, and two as entities, and then use Tb_userrole's navigation properties to handle Tb_users and Tb_ ROLE, this approach is theoretically possible, but bloggers feel that many projects like the navigation properties are considered to be inefficient, so there are some risks involved in using them. Or that sentence, depending on the circumstances, if your project to use storage basic can take care of the requirements, or you do not want to introduce a concept of what Domain Services, you can also follow your design, this is not a problem, after all, the best architecture is suitable for the project architecture!
C # Advanced Series--DDD field drive Design (vi): Domain Services