Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Domain Driven Design - How to handle updates for parts of your aggregrate roots

BACKGROUND: I have a Person domain object. It is an aggregate root. I have included a portion of the class below.

I am exposing methods to perform the objects behaviors. For instance, to add a BankAccount I have the AddBankAccount() method. I have not included all the methods of the class but suffice to say that any public property must be updated using a method.

I am going to create an IPerson repository to handle the CRUD operations.

public interface IPersonRepository
{ 
    void Save(Person p);
    //...other methods
}

QUESTION: How do I tell the repository which fields need to be updated when we are updating an existing person? For example, If I add a bank account to an existing person how do I communicate this information to the repository when repository.Save() is called?

In the repository it is easy to determine when a new person is created, but when an existing person exists and you update fields on that person, i'm not sure how to communicate this to the repository.

I don't want to pollute my Person object with information about which fields are updated.

I could have separate methods on the repository like .UpdateEmail(), AddBankAccount() but that feels like overkill. I would like a simple .Save() method on the repository and it determines what needs to update in some manner.

How have others handled this situation?

I have searched the web and stackoverflow but haven't found anything. I must not be searching correctly because this seems like something simple when it comes to persistence within the DDD paradigm. I could also be way off on my understanding of DDD :-)

public class Person : DomainObject
{
    public Person(int Id, string FirstName, string LastName,
        string Name, string Email)
    {
        this.Id = Id;
        this.CreditCards = new List<CreditCard>();
        this.BankAccounts = new List<BankAccount>();
        this.PhoneNumbers = new List<PhoneNumber>();
        this.Sponsorships = new List<Sponsorship>();
    }

    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public string Name{ get; private set; }
    public string Email { get; private set; }
    public string LoginName { get; private set; }

    public ICollection<CreditCard> CreditCards { get; private set; }

    public ICollection<BankAccount> BankAccounts { get; private set; }

    public ICollection<PhoneNumber> PhoneNumbers { get; private set; }  

    public void AddBankAccount(BankAccount accountToAdd, IBankAccountValidator bankAccountValidator)
    {
        bankAccountValidator.Validate(accountToAdd);

        this.BankAccounts.Add(accountToAdd);
    }

    public void AddCreditCard(CreditCard creditCardToAdd, ICreditCardValidator ccValidator)
    {
        ccValidator.Validate(creditCardToAdd);

        this.CreditCards.Add(creditCardToAdd);
    }

    public void UpdateEmail(string NewEmail)
    {
        this.Email = NewEmail;
    }
like image 613
RDotLee Avatar asked Oct 16 '13 15:10

RDotLee


People also ask

What is aggregate root in domain-driven design?

Aggregate Root is the mothership entity inside the aggregate (in our case Computer ), it is a common practice to have your repository only work with the entities that are Aggregate Roots, and this entity is responsible for initializing the other entities. Consider Aggregate Root as an Entry-Point to an Aggregate.

Can you have multiple aggregate roots?

A single Bounded Context can include many aggregate roots, or we can organise a single aggregate root into a single Bounded Context. An Order aggregate root, consists of entities such as OrderLine, Customer and Product, and value objects such as ShippingAdress, PaymentMethod etc.

Can an aggregate root reference another aggregate root?

[Evans] states that one Aggregate may hold references to the Root of other Aggregates. However, we must keep in mind that this does not place the referenced Aggregate inside the consistency boundary of the one referencing it. The reference does not cause the formation of just one whole Aggregate.

How do you choose the aggregate root?

When choosing an aggregate root you choose between Transactional Consistency and Eventual Consistency. When your business rules allow you would rather favour Eventual Consistency.


1 Answers

There is an example of Repository interface from S#arp Architecture project. It is similar to PoEAA Data Mapper because it used to CRUD operations also.

public interface IRepositoryWithTypedId<T, IdT>
{
    T Get(IdT id);    
    IList<T> GetAll();    
    IList<T> FindAll(IDictionary<string, object> propertyValuePairs);    
    T FindOne(IDictionary<string, object> propertyValuePairs);    
    T SaveOrUpdate(T entity);    
    void Delete(T entity);    
    IDbContext DbContext { get; }
}

As you can see, there is no update method for specific properties of an entity. The whole entity is provided as an argument into the method SaveOrUpdate.

When properties of your domain entity are being updated you should tell your Unit of Work that entity is 'dirty' and should be saved into storage (e.g. database)

PoEAA Unit of Work

You should not pollute your Person object with information about updated fields but it is needed to track information if entity is updated.

There might be methods of the class DomainObject which tell 'Unit of Work' if entity is 'new', 'dirty' or 'deleted'. And then your UoW itself might invoke proper repository methods - 'SaveOrUpdate' or 'Delete'.

Despite the fact that modern ORM Frameworks like NHibernate or EntityFramework have their own implementations of 'Unit of Work', people tend to write their own wrappers/ abstractions for them.

like image 140
Ilya Palkin Avatar answered Oct 02 '22 19:10

Ilya Palkin