1

I'm fairly new to DDD but I am trying to cram as much as possible as fast as possible. I followed https://github.com/dotnet-architecture/eShopOnContainers as a guide for how to structure my code with Mediatr and EF Core.

Fortunately for this application, the persistence and domain model are the same. Unfortunately for me, my data layer does not match our domain model as it is a legacy db.

So i am separating the domain from persistence which is well and good. But I am having a hard time understanding where if i do this code block in a command handler(trying to make it simple and clear)...

var aggregate = repo.GetById(1234);
aggregate.AddItemToList(item);
repo.SaveChanges();

How can i cause the underlying database context of the repo to be aware of the changes that were applied. Only thing i can think is to have a repo.Update(aggregate) call, that would then try to apply db calls to update various places of the db.

This seems like a smell to me.

Any insights would be great.

Thank you!

Edit: Should the repository pattern with a separate Domain and Persistence layer return the presistance layer's model or the domain's?

For example: I have a aggregate Company. And i have a database table called CompanyLegacy which is modeled in the persistence layer using entity framework core.

Should my repository be CompanyLegacyRepository or CompanyRepository? If CompanyRepository, that would mean i query the CompanyLegacy table, and map it to a Company domain model then return it. This model, would not be change tracked. This is where my issue comes from.

But if I'm supposed to do a CompanyLegacyRepository then it seems like that doesn't adhere to DDD guidelines where all actions to be applied to the aggregateroot.

Hooman Bahreini
  • 11,018
  • 7
  • 41
  • 74
Matthew Hartz
  • 231
  • 1
  • 12
  • That's the job of ORM, such as Entity Framework, Dapper, etc... – Hooman Bahreini May 05 '20 at 23:00
  • Right, the ORM is part of my persistance layer. Repo.GetById will use a dbcontext to load the data from the db, the map it to a domain object. Then addItemToList will insert into said domain object. Should i do repo.Update(aggregate) to use the ORM to determine what changed? – Matthew Hartz May 05 '20 at 23:04
  • Could you please tag the ORM that you are using. Also it sounds like your question is more related about saving data using a certain ORM than DDD, would be good clarify the question. – Hooman Bahreini May 05 '20 at 23:12
  • Done, thanks @HoomanBahreini – Matthew Hartz May 05 '20 at 23:13
  • Your code does not look right to me... `GetById(1234)` should return an entity... normally you add an item to a `List` not an `Entity`. But to answer your question, EntityFramework has a Tracking option that if you use can track the changes made to an entity and when saving the the changes, EF would update the DB. – Hooman Bahreini May 05 '20 at 23:16
  • I added an edit if that helps clear some things up. :) – Matthew Hartz May 05 '20 at 23:23

1 Answers1

1

Should the repository pattern with a separate Domain and Persistence layer return the persistence layer's model or the domain's?

Repository should return your Domain model. If you are using DTOs (such as CompanyLegacy) in your Infrastructure layer, it is the responsibility of your Repository to map them to your Domain models. Please note that in a Layered Architecture, the Application layer is not supposed to know about the DTOs used in the Infrastructure layer... it's your Domain models which are the heart of your application. See this question which is closely related to yours.

Your Repository should be called CompanyRepository. You can define an interface for this repository like:

public interface ICompanyRepository
{
    // Company is your domain model not DTO (i.e. your legacy model)
    Company GetById(int id);
    void Add(Company);
    void Update(Company);
}

Change Tracking

Entity Framework change tracking has it's limitations, this question is an example of one of those Disconnected Scenarios, where we cannot rely on EF Change Tracking (because of DTOs). The implementation of the above repository would be like:

public CompanyRepository: ICompanyRepository
{
    Private MyDbContext _context;
    public CompanyRepository(MyDbContext myDbContext) { _context = myDbContext; }

    public Company GetById(int id)
    {
        var companyLegacy = _context
            .CompanyLegacy
            .AsNoTracking()
            .Where(c => c.id = id)
            .FirstOrDefault();
        return MyMapper.ToCompany(companyLegacy);
    }

    public void Add(Company company)
    {
        var companyLegacy = MyMapper.ToLegacy(company);
        _context.Add(companyLegacy);
        _context.SaveChanges();
    }

    public void Update(Company)
    {
        var companyLegacy = MyMapper.ToLegacy(company);
        _context.Update(companyLegacy);
        _context.SaveChanges();
    }
}

This tutorial is helpful for more advanced operations and you can find more info about EF Core change tracking here.

this answer is related to EF 4/5/6 (not core) but gives you some idea about using unique identifier to decide if an entity should be Added or Updated.

Hooman Bahreini
  • 11,018
  • 7
  • 41
  • 74
  • Thank you for this thorough answer. This validates that I was on the right path as I am working on it in this way. One small question I do have, is if an aggregate has some relational properties (1 to many relationships), I could in theory have multiple repositories for the various aggregates. For example, CompanyToEmployeeRepository and CompanyToSalesRepository. Does that make sense? That way instead of having 1 aggregate with sales and employees that has to update a bunch of tables, i can have a finely tuned aggregate for the task at hand. – Matthew Hartz May 06 '20 at 16:33
  • The section called "Change Tracking" is confusing because the code does not do change tracking. This code will update all columns in the DB, regardless of the changes in Company. I always found very tricky to get EF to properly update related entities (especially deletes) and unless you know very well how EF works, you end up with nasty bugs (entities not deleted, orphaned, etc). So, I prefer safer approaches. – Francesc Castells May 06 '20 at 19:33
  • @MatthewHartz if you do these repositories you'll defeat the purpose of Aggregates. Aggregates are transactional boundaries. The Aggregate must ensure that the whole state is valid (root + all relations). To do so, all changes have to be done through the Aggregate root and stored transactionally. If you have a repository that allows you to modify an entity of an aggregate without going through the root, you don't have an aggregate anymore. Also, you should add an optimistic concurrency check on the root and always modify the root, even if it's only to udpate the version column, otherwise – Francesc Castells May 06 '20 at 19:37
  • concurrent updates will eventually break the consistency of your aggregates. – Francesc Castells May 06 '20 at 19:39
  • @MatthewHartz: it depends how you implement your [1-to-many relationship](https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx)... for example your `Company` entity could have a `List`... or your `Employee` entity could have a `Company` property... if you choose to go with either of these solutions, then you wouldn't need a `CompayToEmployee` entity... IMO it's better NOT to create an extra entity in this case. But to answer your question, yes you need one repository per entity (or to be more precise, one repository per aggregate) – Hooman Bahreini May 06 '20 at 22:29
  • @FrancescCastells: you are right, my choice of title is a bit sloppy... Since this is a disconnected environment, we need to manually set the entity state... so, we cannot rely on EF Change Tracking – Hooman Bahreini May 06 '20 at 23:15