Repository, Iunitofwork and Idbcontext

Source: Internet
Author: User

DDD Domain Driven design-talk about the practice of repository, iunitofwork and Idbcontext

Previous: "DDD field-driven design-talk about Repository, iunitofwork and Idbcontext practices (1)"

Read the catalogue:

    1. Pull away from irepository and transform Repository

    2. Changes in Iunitofwork and application Service

    3. Summary of three design options

Briefly summarize the two improvements made in the previous article:

    • Idbcontext are extracted from Repository and unitofwork, and they depend only on idbcontext.
    • Repository and unitofwork for the lateral relationship, unitofwork is responsible for maintaining the object state (increased deletion), Repository is responsible for acquiring the object (check).

Later, the park friend Qlin in the comments, proposed another way, roughly:

    • Repository and unitofwork are still dependent on idbcontext.
    • Unitofwork only Commit,repository provides all the operations of the object (add and revise).

This is the way we do it, about the design of Repository, Iunitofwork and Idbcontext, and the invocation of the application Service, which are two designs, plus one of the options mentioned at the beginning of the previous blog post. I have roughly summed up three kinds, the pros and cons of them, the article finally I summarize.

In addition, the interface design about Idbcontext is actually somewhat vague, because it does not really decouple ef, such as DbSet<TEntity> Set<TEntity>() still dependent on EF, no way, just as we return IQueryable in Repository, you application Servic E call, also must refer to EF, for Idbcontext, we think of it as a data context container, the persistence of all objects is finally done through it, because our solution can only use EF temporarily, so for Idbcontext, Let's design this for the time being.

Let's start with the design.

1. Pull away from the irepository and transform the Repository

What do you mean, pulling away from irepository? Let's look directly at the code:

NamespaceDdd.Sample.Domain.irepository{PublicInterfaceirepository<taggregateroot> where taggregateroot: class, iaggregateroot { void  Add(Taggregateroot aggregateroot); void Update(taggregateroot aggregateroot); void Delete(taggregateroot aggregateroot); taggregateroot Get(int id);}}            

IRepository is a generic interface, the type is iaggregateroot, we define in the inside of the common operation of adding and removing changes, its role is to reduce Repository redundant code, we look at the definition of istudentrepository:

namespace DDD.Sample.Domain.IRepository{ public interface IStudentRepository : IRepository<Student> { Student GetByName(string name); }}

Istudentrepository needs to inherit irepository and determine that the generic type is student,student inherited from Iaggregateroot, because additions and deletions are already defined for common operations, so we have other similar There is no need to define it in Istudentrepository.

IRepository needs to be implemented, and if implemented in Studentrepository, it has no effect, so we need a baserepository to implement IRepository:

NamespaceDdd.Sample.repository{PublicAbstractClassbaserepository<Taggregateroot>:irepository<Taggregateroot>whereTaggregateroot:ClassIaggregateroot {PublicReadOnly Idbcontext _dbcontext;PublicBaserepository(Idbcontext DbContext) {_dbcontext = DbContext;}PublicvoidAdd(Taggregateroot aggregateroot) {_dbcontext.set<taggregateroot> (). ADD (Aggregateroot); }public void Update(taggregateroot aggregateroot) {_dbcontext.entry<taggregateroot> ( Aggregateroot). state = entitystate.modified; } public void Delete(taggregateroot aggregateroot) {_dbcontext.set<taggregateroot> (). Remove (Aggregateroot); Public taggregateroot Get(int id) { return _dbcontext.set<taggregateroot> (). FirstOrDefault (t = t.id = = Id); } }}

I see Baserepository a bit like our unitofwork, because we put the additions and deletions in Repository, because Repository or unitofwork for the same-class relationship, so we used in the Repository IDb Context rather than iunitofwork, this is no problem, we look at the specific implementation of Studentrepository:

NamespaceDdd.Sample.repository{ Public class studentrepository: baserepository<student>  istudentrepository { public studentrepository(idbcontext dbContext): base(dbContext) {}  Public Student getbyname(string name) { return base._dbcontext.set<student> (). Where (x = X.name = = Name). FirstOrDefault (); } }}

Studentrepository is very simple, because the common operation Baserepository has been implemented, base(dbContext) the role is to inject Baserepository Idbcontext object.

Repository's transformation is basically these, the surface looks really good, in addition, if there is no iunitofwork and application Service, we do unit testing of Domain can meet our needs, but need to idbcontext And then make the changes.

2. Changes in Iunitofwork and application Service

Let's take a look at the iunitofwork changes and put the code directly:

namespace DDD.Sample.Infrastructure.Interfaces{ public interface IUnitOfWork { bool Commit(); void Rollback(); }}

Because adding and removing changes are moved to Repository, so iunitofwork work is very simple, only Commit and Rollback, implementation is relatively simple, we look at:

NamespaceDdd.Sample.infrastructure{Publicclass unitofwork: IUnitOfWork {private idbcontext _dbcontext; public UnitOfWork (Idbcontext dbContext) {_dbcontext = DbContext;} public bool commit () {return _ Dbcontext.savechanges () > 0;} public void rollback () {throw new notimplementedexception (); } }}  

This is nothing to say, we look directly at the application Service code:

NamespaceDdd.Sample.application{PublicClassStudentservice:Istudentservice {Private Iunitofwork _unitofwork;Private Istudentrepository _studentrepository;Private Iteacherrepository _teacherrepository;PublicStudentservice(Iunitofwork unitofwork, Istudentrepository studentrepository, Iteacherrepository teacherrepository) {_unitofwork = unitofwork; _studentrepository = studentrepository; _teacherrepository = teacherrepository;}Public Student Get(int id) { return _studentrepository.get (ID);} public bool Add(string name) { var student = New Student {name = name}; var teacher = _teacherrepository.get (1); teacher. studentcount++; _studentrepository.add (student); _teacherrepository.update (teacher); return _unitofwork.commit ();} }}

Studentservice actually change little, just add the original unitofwork modified operation, changed to Studentrepository and _teacherrepository, executed under Studentservice.add Unit test code, found that execution does not pass, for what? Because the idbcontext of Repository and unitofwork are not the same object, the addition of the modified object is registered to Idbcontext through Repository, and the last Unitofwork execution is another idbcont Ext, so we need to make sure that Repository and Unitofwork share a Idbcontext object, how do we do that?

We carry out the transformation under:

NamespaceDdd.Sample.application{PublicClassStudentservice:Istudentservice {Private Idbcontext _dbcontext;Private Iunitofwork _unitofwork;Private Istudentrepository _studentrepository;Private Iteacherrepository _teacherrepository;PublicStudentservice(Idbcontext DbContext) {_dbcontext = DbContext;}Public StudentGet(int id) {_studentrepository =New Studentrepository (_dbcontext);return _studentrepository.get (ID);} public bool Add(string name) {_unitofwork = new Unitofwork (_dbcontext); _studentrepository = 
                                  
                                   new studentrepository (_dbcontext); _teacherrepository = 
                                   new Teacherrepository (_dbcontext); var student = New Student {name = name}; var teacher = _teacherrepository.get (1); teacher. studentcount++; _studentrepository.add (student); _teacherrepository.update (teacher); return _unitofwork.commit ();} }}
                                  

The above corresponding test code execution through, in fact, the solution is very simple, is to manually inject Unitofwork, studentrepository and teacherrepository the same Idbcontext object, of course, this is a solution, There are people who like to use attribute injection, which is all right, and the last thing is to let Repository and Unitofwork share a Idbcontext object.

The relevant code of this article has been submitted to GitHub, you can refer to the following: Https://github.com/yuezhongxin/DDD.Sample

3. Summary of three design options

About the design of Repository, Iunitofwork and Idbcontext, as well as the call of application Service, I summed up three design methods, I think it is also a few of the ways we commonly used, below I basically say separately.

1. Application Service, Repository, Efunitofwork, Iunitofwork,

This design should be most familiar to us, because we designed it from the beginning, but the problem is the most, otherwise I will not write a blog post, such as the existing problems:

    • Iunitofwork's responsibilities are unclear.
    • Repository's responsibilities are unclear.
    • The application Service is confused because it doesn't know who to use.
    • The code for the application Service is getting messy.
    • ....

The last blog post was analyzed as Iunitofwork's design problem, because it did too much, and Repository relies on iunitofwork so that finally in the application Service call, Repository is very redundant, the biggest problem with this design is unclear responsibilities .

2. Application Service, Unitofwork/repository, Idbcontext-iunitofwork/irepository (only query)

The second design is more inclined to me, because the first design problems, so I iunitofwork design very important, and I read the "Enterprise Application Architecture Model" in the unitofwork of all the content, in fact, a few words can be summed up: the maintenance of the object state, Commit changes uniformly. I personally think that the most important part of architecture design is the design of the bottom interface, as we build a skyscraper, if the foundation is not stable, the final result must be collapsed, so, I would like to stick to the design of Iunitofwork:

In contrast to the first design, there is a difference between iunitofwork and irepository as a lateral relationship, why is this design? Because we can't provide query operations through Iunitofwork, and iunitofwork and ORM don't matter, we finally pull out a idbcontext and use EF to implement it.

IRepository only query, this is our definition, in the application Service invocation, the object's new and modified is implemented through Iunitofwork, because the query does not need to record the state, so we do not need to idbcontext in Sharing between Iunitofwork and IRepository, some would say, IRepository should provide the domain object's additions and deletions to the operation Ah, we look at the definition of Repository: the coordination domain and the data mapping layer, using a collection-like interface to access the domain object.

The collection accesses the domain object, that Repository if it is designed like this:

public class StudentRepository : IStudentRepository{    private IQueryable<Student> _students; public StudentRepository(IDbContext dbContext) { _students = dbContext.Set<Student>(); } public Student GetByName(string name) { return _students.Where(x => x.Name == name).FirstOrDefault(); }}

This Repository design is more consistent with the definition, in addition, if we unit test domain, the collective nature of the domain objects can be maintained, but not persisted.

Overall, the biggest advantage of the second design is clear responsibility , you do not want to do bad things (because the interface is already constrained), there is no problem found.

3. Idbcontext-Iunitofwork (only commit)/irepository, unitofwork/repository, Application Service

The third kind of design is this blog, it is actually from the first and the second between the middle value, do some compromise work, concrete implementation, the above has been described in detail, I most can not accept the changes to the iunitofwork, although the surface looks good, but I always feel something is wrong with the place, Just as we are "forced to do something against the moral", may not be aware of anything now, but out of the mix is always to be.

Regarding the design of Repository, Iunitofwork and Idbcontext, and the invocation of application Service, I think it should be one of the most common problems in the design process of DDD architecture, but it is also one of the most puzzling problems, For example, the last two friends wrote the blog:

    • On the distribution and implementation of code between Repository, iunitofwork in the domain layer and application service layer
    • A little bit about MVC EF architecture and Repository model

For this blog post, if you have any questions or questions, please discuss learning. :)

Repository, Iunitofwork and Idbcontext

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.