Сущности
Давайте начнем с сущности Order
.Порядок - это автономный объект, который не зависит от «родительского» объекта.В доменном дизайне это называется агрегатный корень ;это корень всей совокупности заказов.Агрегат заказа состоит из корня и нескольких дочерних объектов, которые в данном случае являются OrderLine
сущностями.
Корень агрегата отвечает за управление всей совокупностью, включая время жизни дочерних сущностей.Другие компоненты не имеют доступа к дочерним объектам;все изменения в совокупности должны проходить через корень.Кроме того, если корень перестает существовать, то и дочерние элементы, то есть строки заказа не могут существовать без родительского заказа.
Customer
также является совокупным корнем.Это не часть заказа, это связано только с заказом.Если заказ перестает существовать, клиент не делает.И наоборот, если клиент перестает существовать, вы захотите сохранить заказы для целей бухгалтерского учета.Поскольку Customer
относится только к вам, вы должны иметь только CustomerId
в заказе.
class Order
{
int OrderId { get; }
int CustomerId { get; set; }
IEnumerable<OrderLine> OrderLines { get; private set; }
}
Хранилища
OrderRepository
отвечает за загрузку всего Order
агрегат или его части, в зависимости от требований.Он не несет ответственности за загрузку клиента.Если вам нужен клиент, загрузите его из CustomerRepository
, используя CustomerId
из заказа.
class OrderRepository
{
Order GetById(int orderId)
{
// implementation details
}
Order GetById(int orderId, OrderLoadOptions loadOptions)
{
// implementation details
}
}
enum OrderLoadOptions
{
All,
ExcludeOrderLines,
// other options
}
Если вам когда-нибудь понадобится загрузить строки заказа после этого, вы должны использовать скажи, не спрашивай принцип.Сообщите заказу, чтобы загрузить его строки заказа и какой репозиторий использовать.После этого заказ сообщит хранилищу информацию, которую ему необходимо знать.
class Order
{
int OrderId { get; }
int CustomerId { get; set; }
IEnumerable<OrderLine> OrderLines { get; private set; }
void LoadOrderLines(IOrderRepository orderRepository)
{
// simplified implementation
this.OrderLines = orderRepository.GetOrderLines(this.OrderId);
}
}
Обратите внимание, что код использует IOrderRepository
для извлечения строк заказа, а не отдельный репозиторий для строк заказа.Конструкция, управляемая доменом, гласит, что должен быть репозиторий для каждого совокупного корня.Методы извлечения дочерних сущностей принадлежат хранилищу корня и должны быть доступны только корню.
Абстрактные / базовые хранилища
Я сам написал абстрактные хранилища с операциями CRUD, но нашелчто это не добавило никакой ценности.Абстракция полезна, когда вы хотите передать экземпляры подклассов в вашем коде.Но какой код будет принимать любую реализацию BaseRepository
в качестве параметра?
Кроме того, операции CRUD могут различаться для каждой сущности, что делает базовую реализацию бесполезной.Вы действительно хотите удалить заказ или просто установить его статус на удаленный?Если вы удалите клиента, что произойдет с соответствующими заказами?
Мой совет: упростите задачу .Держитесь подальше от абстракции и базовых классов.Конечно, все репозитории имеют некоторую функциональность, а дженерики выглядят круто.Но вам на самом деле нужно это?