Моделирование предметной области: все правильно
Посмотрев отличное видео Джимми Богарда о создании злых доменов, я попытался применить те же принципы к одному из моих существующих проектов, чтобы оценить, насколько хорошо я понял концепцию. У меня есть мои вопросы и сомнения, перечисленные ниже.
Домен фона: администратор может просматривать список компаний. Затем он утверждает компанию. Предполагается, что база данных обновит логическое поле до true и сохранит идентификатор пользователя, который утвердил компанию.
Первоначально у меня был следующий код, написанный на моем слое сервиса. Он передает запрос в хранилище, которое обновляет соответствующие поля в базе данных, а затем отправляет почтовое уведомление.
public void ApproveCompany(int companyId, int userId)
{
_companyRep.ApproveCompany(companyId, userId);
//send mail to company representatives on successful approval.
}
Рефакторинг для создания богатых доменов и инкапсуляции логики в классе домена, я создал ниже.
public void ApproveCompany(int companyId, int userId)
{
var user = _userRep.GetById(userId);
var company = _companyRep.GetById(companyId);
user.Approve(company);
_companyRep.Insert(c);
//send mail to company representatives on successful approval.
}
public class AdminUser
{
public string Name { get; set; }
public void Approve(MyApprovedCompany c)
{
c.SetIsApproved(this);
}
}
public class Company
{
public bool IsApproved { get; private set; }
public AdminUser ApprovedBy { get; private set; }
public void SetIsApproved(AdminUser user)
{
if (this.IsApproved)
throw new Exception("This company has already been approved by user: " + user.Name);
this.IsApproved = true;
this.ApprovedBy = user;
}
}
Запросы:
- Что я сделал полностью правильно / частично правильно?
- С точки зрения производительности, выборка двух объектов только для того, чтобы создать надлежащие экземпляры классов, будет проблемой в будущем?
- Относится ли почтовое уведомление к сервисному слою?
- Как будет выглядеть репозиторий моей компании для работы со свойством, связанным с пользователем, который утверждает компанию? Если в репозитории моей компании есть ссылка на репозиторий пользователей (я думаю, что это неправильно).
В качестве альтернативы, я мог бы написать слой обслуживания, как показано ниже, но я не думаю, что это тоже правильно.
public void ApproveCompany(int companyId, int userId)
{
var user = _userRep.GetById(userId);
var company = _companyRep.GetById(companyId);
if(company.IsApproved)
{
throw new Exception("This company has already been approved by user: " + _userRep.GetById(company.ApprovedUserId).Name);
}
else
{
user.Approve(company);
_companyRep.Insert(c);
}
}
1 ответ
На такие вопросы почти невозможно ответить правильно, однако вот что я могу сказать вам из того, что я вижу:
Это несколько правильно, но я обычно предпочитаю размещать поведение на AR, на который ориентирована операция, а не на актера, выполняющего ее. Двойная диспетчеризация не приносит в этом случае ничего полезного ИМХО. Поэтому я бы упростил
company.Approve(adminUser)
, Вы могли бы сказать, чтоadminUser.approve(Company)
лучше отражает вариант использования, такой как "Администратор утверждает компанию", но вы можете просто перевернуть его и сказать, что "компания одобрена администратором". Также обратите внимание, чтоcompany.SetIsApproved
Ваш метод очень ориентирован на CRUD и, конечно, не очень хорошо отражает ваш вездесущий язык.AR должны быть разработаны как можно меньше. Пока вы не создаете ненужные большие кластерные агрегаты, я не вижу в этом проблемы. Я настоятельно рекомендую вам прочитать " Эффективный совокупный дизайн" Вона Вернона.
В идеале вы должны полагаться на доменные события для реализации побочных эффектов операции. Существует много информации о том, как реализовать доменные события в Интернете. Однако из-за отсутствия механизма публикации / подписки это может быть сделано на уровне службы приложений или вы можете внедрить почтовую службу на уровне метода AR.
Проблема в том, что вы ссылаетесь на AR внутри другого AR. AR обычно должны ссылаться на другие AR по идентичности. Следовательно,
Company
не будет держатьсяAdminUser
, только по идентификатору пользователя. Таким образом, ваша проблема исчезнет, и вы уменьшите размер AR.