Совокупные и совокупные корни
В реальных приложениях явная Aggregate
реализация не требуется. Aggregate
– это набор связанных entities
, ограниченных aggregation relation
, с заданным основным entity
, называемым Aggregate Root
.
Вам просто нужно решить, что entities
является aggregate roots
, а затем спроектировать свои классы и агрегаты в приложении с aggregate root encapsulation and other rules
.
С учетом этого я предлагаю следующие интерфейсы и классы
interface IAggregateRoot<TKey>
{
TKey Key { get; }
}
//entity which is aggregate root
class Car : IAggregateRoot<int>
{
int Key { get; }
Ienumerable<Door> Doors { get; }
}
//other entity, not aggregate root
class Door
{
string Colour { get; set; }
}
//generic interface with IAggregateRoot constraint
interface IRepository<TAggregateRoot, TKey> where TAggregateRoot : IAggregateRoot
{
TAggregateRoot Get(TKey key)
//other methods
}
Фабрики и хранилища
И factories
, и repositories
технически создают экземпляры aggregate roots
и другие, ограниченные агрегатом entities
. Оба строят aggregate
, но имеют разную семантику.
Репозиторий
Repository
восстанавливает aggregate
из постоянного хранилища (с учетом модели хранения) и предполагает, что агрегат находится в консистентном состоянии, то есть агрегатные инварианты не нарушены.
Если есть инвариантное нарушение, то Repository
приходится делать какие-то действия, т.к. уже созданный агрегат несовместим (?!).
Фабрика
Factory
больше похож на строителя. Он строит aggregate
со знанием своих инвариантов. Во время построения aggregate
aggregate
может находиться в несогласованном состоянии, но по мере завершения процесса построения aggregate
он должен соблюдать инварианты и защищать их.
Если по каким-то причинам происходит нарушение инварианта, factory
не может построить aggregate
и отбрасывает его.
Иногда репозитории используют фабрики для создания экземпляров, но в большинстве случаев я считаю, что лучше их полностью разделить.
Пример
Один пример того, как может работать фабрика.
Предположим, что инвариантом класса Car является «Автомобиль может иметь только 2 или 4 двери».
public interface ICar : IAggregateRoot<int>
{
int Key { get; }
IEnumerable<IDoor> Doors { get; }
SetDoors(IEnumerable<IDoor> doors);
}
public class Car : ICar
{
//take a look here, it's internal
internal IList<IDoor> Doors { get; set;}
public int Key { get; set; }
public IEnumerable<IDoor> Doors { get { return Doors; } }
//aggregate root always controls aggregate invariants
public SetDoors(IEnumerable<IDoor> doors)
{
if (doors.Count() != 2 || doors.Count() != 4)
throw new ApplicationException();
Doors = doors.ToList();
}
}
interface IDoor { }
class Door : IDoor { }
//generic interface with IAggregateRoot constraint
interface ICarFactory
{
ICar CreateCar(int doorsCount)
}
public class CarFactory : ICarFactory
{
public ICar CreateCar(int doorsCount)
{
if (doorsCount != 2 && doorsCount != 4)
throw new ApplicationException();
var car = new Car();
car.Key = 1;
car.Doors = new List<IDoor>();
car.Doors.Add(new Door());
//now car is inconsistent as it holds just one door
car.Doors.Add(new Door());
//now car is consistent
return car;
}
}
Это искусственный пример, но иногда агрегатные инварианты слишком сложны, чтобы удовлетворять их всегда в процессе построения.
Теперь предполагается, что Factory и Aggregate Root имеют тесные связи, поскольку Factory знает о внутренних полях Car.
Наконец, я думаю, что вы неправильно понимаете некоторые концепции, и рекомендую вам прочитать книгу Evans DDD.
РЕДАКТИРОВАТЬ: Возможно, я упускаю тот факт, что MyAggregate на самом деле является MyAggregateRoot. Но для меня странно смешивать эти понятия. Более того, на SO был один вопрос с явным искусственным классом Aggregate, содержащим все агрегатные внутренности только для того, чтобы показать границы агрегатов. Я предпочитаю приемлемое понятие AggregateRoot, как и большинство диаграмм, которые я видел http://ptgmedia.pearsoncmg.com/images/chap10_9780321834577/elementLinks/10fig05.jpg
person
Valentin P.
schedule
08.01.2015
Repository.Add()
), но, возможно, вы можете избежать этого, просто добавив новый идентификатор совокупного корня в список идентификаторов, как это рекомендуется для отношений между агрегатами. Не знаю, как с этим справятся различные ORM. - person guillaume31   schedule 09.01.2015