Study on the driving design of DDD Domain (ii): Warehousing repository (UP)
Preface: The previous chapter introduces the aggregation division in the DDD design demo and the design of the entity and the aggregation root, which continues to say that one of the most controversial topics in DDD is the warehousing repository, why repository has such a big controversy, Bloggers believe that the main reason is nothing less than the following two points: First, Repository's true intentions did not understand clearly, resulting in the design of the disorder, with the project's horizontal and vertical expansion, to the end more and more difficult to maintain; second, the fashion for "mode" and "mode", warehousing is not suitable for all projects, It's like there's no architecture that solves all the design challenges. This article through the design of the demo to talk about the Bo Master of the understanding of warehousing, there is no place to look at the Garden friends treatise!
First, the definition of warehousing
Warehousing, as the name implies, stores data in warehouses. Then some people are puzzled, since we have a database to access data, why also to get a concept of warehousing, in fact, bloggers think this is a different level of consideration, the database is mainly used to access data, and one of the warehousing role is used for data persistence. At the architectural level, warehousing is used to connect the domain layer and infrastructure layer, and the domain layer accesses the storage mechanism through warehousing without worrying too much about the specifics of the storage mechanism. According to DDD design principles, the aggregation root of the domain model of the object of warehousing, i.e. each aggregation has a separate warehousing. It may be said that we can not understand, I believe that the storage of the code design, we can have a more thorough understanding.
Second, the significance of the use of warehousing
1, standing in the domain layer more concerned about the domain logic level, said, warehousing as the domain layer and the infrastructure layer of the connecting components, so that the domain layer does not have to pay much attention to the storage details. At the design time, the warehousing interface is placed in the domain layer, and the specific implementation of the warehousing in the infrastructure layer, the domain layer through the interface to access data storage, Instead of paying too much attention to the details of the storage data (that is, the domain layer does not have to worry about whether you use EntityFramework or NHibernate to store the data), this allows the domain layer to place more focus on the domain logic.
2, at the level of architecture, warehousing decoupling the domain layer and ORM between the link, this is a lot of people design warehousing mode, for example, we want to replace the ORM framework, we only need to change the implementation of the warehouse, for the domain layer and storage interface basically do not need to make any changes.
Three, code example 1, Solution structure diagram
It said that the design of the warehouse is the interface and the realization of separation, so our storage interface and work unit interface are all placed in the domain layer, A new warehouse Implementation class library estm.repository is created at the infrastructure level, which needs to add a reference to the domain layer to implement the warehousing interface and the work unit interface of the domain layer. Therefore, through the field layer can see the irepositories inside the storage interface and infrastructure Layer Estm.repository project under the Repositories warehouse implementation is one by one corresponding. Let's take a look at the specific code design. In fact, the garden has a lot of classic warehouse design, in order to better illustrate the role of warehousing, Bo master or swim down ~ ~
2. Storage interface
<summary>/// Warehousing interface, definition of common generics Grud///</summary>// <typeparam name= "TEntity" > Generic aggregation root , since warehousing in DDD can only operate on aggregate roots </typeparam> public interface irepository<tentity> where tentity:aggregateroot { #region property iqueryable<tentity> entities {get;} #endregion #region Public method int Insert (TEntity entity); int Insert (ienumerable<tentity> entities); int Delete (object ID); int Delete (TEntity entity); int Delete (ienumerable<tentity> entities); int Update (TEntity entity); TEntity Getbykey (object key); #endregion }
<summary>/// Department of Aggregation root Storage interface/// </summary> public Interface Idepartmentrepository:i Repository<tb_department> { }
<summary>/// Menu This aggregation root's warehousing interface/// </summary> public Interface Imenurepository:irepository <TB_MENU> { ienumerable<tb_menu> getmenusbyrole (tb_role orole); }
<summary>// roles This aggregation root's warehousing interface/// </summary> public Interface Irolerepository:i Repository<tb_role> { }
<summary>// user This aggregation root storage interface/// </summary> public Interface Iuserrepository:i Repository<tb_users> { ienumerable<tb_users> getusersbyrole (tb_role orole); }
In addition to irepository this generic interface, the other 4 storage interfaces are built for aggregation interface, the first chapter of C # Advanced series--DDD Field Drive Design (a): aggregation introduces the division of aggregation, where the warehousing interface is based on this establishment. The Iuserrepository interface implements the IRepository interface and passes the corresponding aggregation root into the generic type, which is applied to the design of the last chapter aggregation root.
3. Warehousing Realization Class
Generic implementation class for warehousing efbaserepository<tentity>: irepository<tentity> where Tentity:aggregateroot { [Import (typeof (Iefunitofwork))] Private iefunitofwork unitofwork {get; set;} Public Efbaserepository () {
Register MEF Regisgter.regisgter (). Composeparts (this); } public iqueryable<tentity> Entities {get {return unitofwork.context.set<tentity> ( ); }} public int Insert (TEntity entity) {unitofwork.registernew (entity); return Unitofwork.commit (); } public int Insert (ienumerable<tentity> entities) {foreach (var. obj in Entities) {unitofwork.registernew (obj); } return Unitofwork.commit (); } public int Delete (object id) {var obj = unitofwork.context.set<tentity> (). Find (ID); if (obj = = null) {return 0; } unitofwork.registerdeleted (obj); return Unitofwork.commit (); } public int Delete (TEntity entity) {unitofwork.registerdeleted (entity); Return Unitofwork.commIt (); } public int Delete (ienumerable<tentity> entities) {foreach (var entity in entities) {unitofwork.registerdeleted (entity); } return Unitofwork.commit (); } public int Update (TEntity entity) {unitofwork.registermodified (entity); return Unitofwork.commit (); Public TEntity Getbykey (object key) {return unitofwork.context.set<tentity> (). Find (key); } }
The generic implementation class for warehousing imports the unit of work through MEF, which has a context object that connects to the database.
[Export (typeof (Idepartmentrepository))] public class Departmentrepository:efbaserepository<tb_department>,idepartmentrepository { }
[Export (typeof (Imenurepository))] public class Menurepository:efbaserepository<tb_menu>,imenurepository {public ienumerable<tb_ Menu> getmenusbyrole (tb_role orole) { throw new Exception (); } }
[Export (typeof (Irolerepository))] public class Rolerepository:efbaserepository<tb_role>,irolerepository { }
[Export (typeof (Iuserrepository))] public class Userrepository:efbaserepository<tb_users>,iuserrepository {public ienumerable<tb_ Users> getusersbyrole (tb_role orole) { throw new notimplementedexception (); } }
Warehousing is a 4 specific implementation class Inside can also be imported through the base class work unit object Unitofwork to operate the database.
4. Working Unit Interface
Work cell base class Interface public interface iunitofwork { bool iscommitted {get; set;} int Commit (); void Rollback (); }
Warehousing Context Work unit interface, the general situation of using this is that there is a transactional operation between multiple warehouses, which is used to mark the additions and deletions of the aggregation root to public interface iunitofworkrepositorycontext:iunitofwork, IDisposable {//<summary>///To mark the status of the aggregation root as new, but the EF context is not submitted at this time///</summary>//&L T;typeparam name= "TEntity" ></typeparam>///<param name= "obj" ></param> void registernew <TEntity> (TEntity obj) where tentity:aggregateroot; <summary>///The status of the aggregation root is marked as modified, but the EF context is not submitted at this time//</summary>//<typeparam name= "Tentit Y "></typeparam>//<param name=" obj "></param> void registermodified<tentity> (TEn Tity obj) where tentity:aggregateroot; <summary>///The status of the aggregation root is marked for deletion, but the EF context is not submitted at this time//</summary>//<typeparam name= "Tentit Y "></typeparam>//<param name=" obj "></param> void registerdeleted<tentity> (TEnt ity obj) where Tentity:aggregateroot; }
See these two interfaces may have some doubts, why should design two interfaces, directly merge one not? This work unit is a source of design ideas from Dax.net's series of articles, again expressing thanks! Indeed, in the beginning, bloggers have this kind of doubt, think carefully to know, should be out of the event mechanism to design, to achieve iunitofworkrepositorycontext this interface is for the storage design of the work unit, The implementation of Iunitofwork this interface in addition to the storage design, there may be other circumstances, such as the event mechanism.
5. Work Unit Implementation Class
Represents the work unit interface for EF, because DbContext is the EF object Public interface Iefunitofwork:iunitofworkrepositorycontext { DbContext context {get;} }
Why design a layer of interfaces here? Because the blogger feels that the work unit is going to introduce EF's context object, so if you're using NH, then this should be the introduction of the Session Object .
<summary>///Work Order implementation class///</summary> [Export (typeof (Iefunitofwork))] public class Efunitofwork : iefunitofwork {#region Property//EF context object exposed through work cell public DbContext context {get {return Efcontex T }} [Import (typeof (DbContext))] public DbContext efcontext {get; set;} #endregion #region Constructor Public efunitofwork () {//Register MEF Regisgter.regisgter (). Composeparts (this); } #endregion #region Iunitofworkrepositorycontext interface public void registernew<tentity> (TEntity ob j) where Tentity:aggregateroot {var state = context. Entry (obj). State; if (state = = entitystate.detached) {context. Entry (obj). state = entitystate.added; } iscommitted = false; } public void registermodified<tentity> (TEntity obj) where tentity:aggregateroot {if (CO NtExt. Entry (obj). state = = entitystate.detached) {context. Set<tentity> (). Attach (obj); } context. Entry (obj). state = entitystate.modified; iscommitted = false; } public void registerdeleted<tentity> (TEntity obj) where Tentity:aggregateroot {context . Entry (obj). state = entitystate.deleted; iscommitted = false; } #endregion #region Iunitofwork interface public bool iscommitted {get; set;} public int Commit () {if (iscommitted) {return 0; } try {int result = context. SaveChanges (); Iscommitted = true; return result; } catch (Dbupdateexception e) {throw e; }} public void Rollback () {iscommitted = false; } #endregion #region IDisposable interface public void Dispose () {if (! iscommitted) {Commit (); } context. Dispose (); } #endregion}
work Unit efunitofwork above the MEF's export, is for warehousing to implement the base class import, in the same vein, here is a point to note, here to import DbContext, then the EF context object will be export.
[Export (typeof (DbContext))] public partial class Estmcontainer:dbcontext { }
This uses the universal partial class, and remembers the domain model mentioned in the previous chapter, and is defined by some classes on the basis of the EDMX. Similarly, there must be an EF auto-generated context object below the EDMX, as follows:
public partial class Estmcontainer:dbcontext {public Estmcontainer () : Base ("Name=estmcontainer") { } protected override void Onmodelcreating (Dbmodelbuilder modelBuilder) { throw new Unintentionalcodefirstexception (); } Public dbset<tb_department> tb_department {get; set;} Public dbset<tb_menu> Tb_menu {get; set;} Public dbset<tb_menurole> Tb_menurole {get; set;} Public dbset<tb_role> Tb_role {get; set;} Public dbset<tb_userrole> Tb_userrole {get; set;} Public dbset<tb_users> tb_users {get; set;} }
The method of registering MEF is used in several places above
Regisgter.regisgter (). Composeparts (this);
is because we define the registration method in the infrastructure layer
Namespace Estm. infrastructure.mef{public class Regisgter {public static Compositioncontainer Regisgter () { Try { Aggregatecatalog aggregatecatalog = new Aggregatecatalog (); string path = AppDomain.CurrentDomain.BaseDirectory; var thisassembly = new Directorycatalog (path, "*.dll"); if (thisassembly.count () = = 0) { Path = path + "bin\\"; thisassembly = new Directorycatalog (path, "*.dll"); } AGGREGATECATALOG.CATALOGS.ADD (thisassembly); var _container = new Compositioncontainer (aggregatecatalog); return _container; } catch (Exception ex) { return null; }}} }
6. Demo Test
In order to test our framework to run through, we write a test method in the application layer. Under normal circumstances, the application Layer ESTM.WCF.Service project only need to add a reference to the Estm.domain project, then how to find the implementation of warehousing in the application layer? or our omnipotent MEF, through the IOC dependency injection, the application layer does not have to add the warehousing implementation layer reference, through the MEF to inject the warehousing implementation into the application layer, but only if the application layer Bin directory under the storage implementation layer generated DLL, You need to set the Estm.repository project's build directory to the bin directory of the ESTM.WCF.Service project. This issue was introduced in the "loosely coupled" (end-of-interface programming) design of the Advanced series--MEF of C # .
Let's take a look at the test code.
Namespace Estm. Wcf. service{ class program { [Import] public iuserrepository userrepository {get; set;} static void Main (string[] args) { var oprogram = new program (); Regisgter.regisgter (). Composeparts (Oprogram); var lstusers = OProgram.userRepository.Entities.ToList ();}} }
Run to get results:
7. Summary
At this point, the general design of our framework warehouse is over, and we look back at the advantages of this design:
(1) The storage interface layer and the implementation layer separation, make the domain model more pure, the domain model only focus on the storage interface, instead of focusing on the specific details of data storage, so that the domain model will be more focused on the field of business.
(2) The application layer only needs to refer to the domain layer, only need to invoke the storage interface inside the domain layer to get the desired data, instead of adding the warehousing specific implementation of the reference, which is exactly in line with the project decoupling design.
(3) Easy to replace the ORM. The project now uses EF, and if you need to change to NH later, you only need to implement a set of warehousing and context. It is important to note that because the entire framework uses EF's model first, in order to directly use the model of EF, we define the EDMX in the domain layer, in fact, it is unreasonable, but we use the partial definition of the domain model directly, If you want to better use the DDD design, EF Now code first is the best way, the domain layer only defines the domain model and focus on the domain logic, EF crud on the infrastructure layer, switch ORM really only need to re-implement a set of warehousing, this design is the blogger really want the effect, Please understand that time and experience are limited. In the future if there is time bloggers will share a fully designed DDD.
Category:. Net, C # Tags: DDD
DDD Domain driven Design warehousing repository