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

BACKGROUND: I have a Person domain object. This is the cumulative root. I have included the part below.

I expose methods for executing the behavior of objects. For example, to add BankAccount, I have the AddBankAccount () method. I did not include all the methods of the class, but suffice it to say that any public property must be updated using the method.

I am going to create an IPerson repository to handle 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 updating an existing person? For example, if I add a bank account to an existing person, how can I transfer this information to the repository when the repository. Called ()?

It is easy to determine in the repository when a new person is created, but when an existing person exists and you update the fields of this person, I am not sure how to report this to the repository.

I do not want to pollute my Person object with information about which fields are updated.

I could have separate methods in the repository, such as .UpdateEmail (), AddBankAccount (), but this is like brute force. I need a simple .Save () method in the repository and it determines what needs to be updated somehow.

How did others deal with this situation?

I searched the web and stackoverflow but didn't find anything. I shouldn't be looking right, because it seems like something simple when it comes to persistence in the DDD paradigm. I could also be aloof from understanding 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; } 
+6
source share
2 answers

There is an example repository interface from the S # arp Architecture project . It is similar to the PoEAA Data Mapper , as it has also been used for CRUD operations.

 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 certain properties of an object. The entire object is provided as an argument to the SaveOrUpdate method.

When the properties of a domain object are updated, you must tell Unit of Work that the object is dirty and should be stored in a repository (for example, a database)

PoEAA Unit of Work

You should not pollute your Person object with information about updated fields, but it is necessary to track information if the object is updated.

There may be methods of the DomainObject class that report “Unit of Work” if the entity is “new,” “dirty,” or “deleted.” And then your UoW ​​itself can call the correct repository methods - "SaveOrUpdate" or "Delete".

Despite the fact that modern ORM structures such as NHibernate or EntityFramework have their own "Unit of Work" implementations, people tend to write their own wrappers / abstractions for them.

+2
source

What I'm doing to solve this problem is adding an interface to my domain objects:

 interface IDirtyTracker { bool IsDirty {get;} void MarkClean(); void MarkDirty(); } 

The base class DomainObject can implement IDirtyTracker , and then repositories, etc. can use IsDirty to check if it is dirty or not.

In each setter that makes the change:

 void SetValue() { this._value = newValue; this.MarkDirty(); } 

This does not give you a fine grain check, but it is an easy way to avoid unnecessary updates at the storage level.

To make this a little easier, you can add the GetPropertiesToIncludeInDirtyCheck method, which will retrieve a list of properties that need to be checked.

 interface IDirtyTracker { IENumerable<Object> GetPropertiesToIncludeInDirtyCheck(); } 
0
source

Source: https://habr.com/ru/post/956096/


All Articles