Бизнес-правило делится на два класса
У меня есть домен распределения проекта со следующими бизнес-правилами
- Когда новый сотрудник получает выделение на проект, общие расходы не должны превышать сумму бюджета.
- Для работника общий процент распределения не должен превышать 100%
Я создал объекты, как показано ниже, созданный в C#
,
ВОПРОС
Allocate
логика разделена на два класса - Project и Employee..The List<Allocation>
передается в качестве параметра в метод Allocate, а не добавляется как свойство класса... Это правильный подход или мне нужно добавить List<Allocation>
как свойство в этих двух классах?
Замечания:
База данных
дАЕТ
Код
проект
public class Project
{
public int ProjectID { get; set; }
public int BudgetAmount { get; set; }
public string ProjectName { get; set; }
public void Allocate(Role newRole, int newPercentage, Employee newEmployee, List<Allocation> existingAllocationsInProject)
{
int currentTotalExpenditure = 0;
if (existingAllocationsInProject != null)
{
foreach (Allocation alloc in existingAllocationsInProject)
{
int allocationExpenditure = alloc.Role.BillRate * alloc.PercentageAllocation / 100;
currentTotalExpenditure = currentTotalExpenditure + allocationExpenditure;
}
}
int newAllocationExpenditure = newRole.BillRate * newPercentage / 100;
if (currentTotalExpenditure + newAllocationExpenditure <= BudgetAmount)
{
List<Allocation> existingAllocationsOfEmployee = GetAllocationsForEmployee(newEmployee.EmployeeID);
bool isValidAllocation= newEmployee.Allocate(newRole, newPercentage, existingAllocationsOfEmployee);
if (isValidAllocation)
{
//Do allocation
}
else
{
throw new Exception("Employee is not avaiable for allocation");
}
}
else
{
throw new Exception("Budget Exceeded");
}
}
}
Работник
public class Employee
{
public int EmployeeID { get; set; }
public string EmployeeName { get; set; }
public bool Allocate(Role newRole, int newPercentage, List<Allocation> existingAllocationsOfEmployee)
{
int currentTotalAllocation = 0;
if (existingAllocationsOfEmployee != null)
{
foreach (Allocation alloc in existingAllocationsOfEmployee)
{
currentTotalAllocation = currentTotalAllocation + alloc.PercentageAllocation;
}
}
if (currentTotalAllocation + newPercentage <= 100)
{
return true;
}
return false;
}
}
Рекомендации
Следующее из шаблона репозитория без ORM
Какое поведение требует от клиента наличия списка заказов? Когда вы больше задумываетесь о поведении вашего домена (то есть какие данные требуются и в какой момент), вы можете смоделировать свои агрегаты на основе вариантов использования, и все станет намного понятнее и намного проще, так как вы отслеживаете изменения только для небольшого набора объектов. в совокупной границе.
Я подозреваю, что Клиент должен быть отдельным агрегатом без списка заказов, а Заказ должен быть агрегатом со списком строк заказов. Если вам необходимо выполнить операции с каждым заказом для клиента, используйте orderRepository.GetOrdersForCustomer(customerID); внесите свои изменения, затем используйте orderRespository.Save(order);
3 ответа
У меня есть несколько комментариев:
Разделение логики размещения - правильная вещь
Подумайте о том, чтобы переместить логику выделения в классы обслуживания, например ProjectService и EmployeeService, чтобы модели предметной области могли не иметь логики
Рассмотрите возможность добавления нового класса AllocationService для управления выделениями.
public void Allocate(Project project, Role role, Employee employee, int percentage)
{
// Fetch allocation from allocation repository
var allocations = _allocationRepository.GetAllocations(project.Id);
// project allocation logic
if (!_projectService.Allocate(Project, Role, int percentage))
{
// throw exception
}
// allocate to employee
if(!_employeeService.Allocate(employee, role, percentage))
{
// throw exception
}
// create new allocation
_allocationRepository.Add(new Allocation
{
......
});
}
Репозиторий распределения и сервисы могут быть введены через конструктор, например
public interface IAllocationRepository
{
IEnumerable<Allocation> GetAllocationsByProject(Project project);
IEnumerable<Allocation> GetAllocationsByEmployee(Employee employee);
void Add(Allocation);
}
IAllocationRepository также может быть внедрен в EmployeeService и ProjectService, так что вам не нужно передавать список распределения.
Логика Allocate разделена на два класса - Project и Employee.
Я бы не стал этого делать, поскольку это разделяет ответственность за распределение, тем самым нарушая принцип единой ответственности. Если вы обнаружите, что он не принадлежит ни Project
ни к Employee
тогда доменная служба может сделать эту работу. В целом, операции с участием нескольких объектов, которые не образуют часть одного и того же агрегата, являются кандидатами на размещение в таком сервисе.
List<Allocation>
передается в качестве параметра в метод Allocate, а не добавляется как свойство класса... Это правильный подход или мне нужно добавитьList<Allocation>
как свойство в этих двух классах?
Мой ответ будет ни один: добавить List<Allocation>
только к вашему Project
учебный класс.
Я думаю, что вы должны рассмотреть, что Allocation
действительно означает в вашем домене. Является ли это лицо, которое является частью совокупности проекта? Может ли это быть даже объект ценности вместо сущности?
Иногда я теряю перспективу домена, когда вокруг меня возникают отношения с базой данных. В этом случае я вижу, что таблица размещения даже не имеет собственного идентификатора; вместо этого, кажется, представляет собой только отношения между Project
, Employee
а также Role
с несколькими атрибутами. Хотя доменная модель не должна заботиться о постоянстве, это может дать некоторые подсказки о том, что Allocation
действительно представляет.
С моей точки зрения, Allocation
имеет смысл только в контексте Project
и, следовательно, он должен быть частью этой совокупности. Можно утверждать, что его равенство не основано на идентичности и, таким образом, это может быть даже объект стоимости. Ответственность за соблюдение первого ограничения - не превышающего бюджет при распределении - принадлежит Project
юридическое лицо, и это может быть выполнено при распределении сотрудников.
Хитрое ограничение является вторым: Employee
не более 100% распределения через несколько Projects
, В этом случае вы можете быть заинтересованы в предоставлении средств для получения этих Projects
для которого данный Employee
выделяется, может быть, через ваш Project
хранилище. Вы также можете предоставить операцию для проверки, чтобы предоставить общее распределение для данного Employee
возможно через доменный сервис.
Обратите внимание, что вы на самом деле делаете всю эту логику в Allocate
метод вашего Project
класс: сначала вы получите все Allocations
через GetAllocationsForEmployee
а затем вы передаете полученный список Employee.Allocate
который на самом деле может быть назван CanBeAllocated
, Вы можете чувствовать, что это ответственность Employee
чтобы обеспечить эту бизнес-логику, но я думаю, что она не имеет ничего общего ни со своими свойствами, ни со своим поведением, и, следовательно, она должна быть частью Project.Allocate
метод или служба домена, если вы продолжаете чувствовать, что есть смешанные обязанности.
В заключение, если в предыдущих комментариях есть некоторая путаница, нет ничего плохого в том, чтобы поместить логику в классы вашей модели, это фактически фундаментальная часть моделирования всей области! Сообщение AnemicDomainModel Мартина Фаулера дает хорошее представление об этом.
Бизнес-правило также относится к существующим распределениям. Как насчет того, чтобы сделать Распределение Агрегатным бизнес-правилом на его Фабрике? лайк:
public Allocation Allocate(Project project, Role newRole, int newPercentage, Employee newEmployee)
{
List<Allocation> existingAllocationsInProject = allocationRepository.findBy(project);
//validate project rule
List<Allocation> existingAllocationsInEmployee = allocationRepository.findBy(newEmployee);
//validate employee rule
}
Поэтому в этом случае нам не нужно беспокоиться о том, как найти существующие распределения. И валидация правил может быть дополнительно реорганизована с использованием шаблонов спецификаций.