【问题标题】:Business Rule Split among two classes两个类之间的业务规则拆分
【发布时间】:2014-01-11 10:19:42
【问题描述】:

我有一个具有以下业务规则的项目分配域

  1. 当新员工被分配到一个项目时,总支出不应超过预算金额。
  2. 对于员工,总分配百分比不应超过 100%

我在C# 中创建了如下所示的实体。

问题

Allocate 逻辑分为两个类 - Project 和 Employee..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;
    }

    }

参考文献

以下来自Repository Pattern without an ORM

有什么行为需要客户有订单列表?当您更多地考虑您的域的行为(即在什么时候需要什么数据)时,您可以根据用例对聚合进行建模,并且事情变得更加清晰和容易,因为您只需跟踪一小部分对象的更改在聚合边界。

我怀疑 Customer 应该是没有订单列表的单独聚合,而 Order 应该是带有订单行列表的聚合。如果您需要对客户的每个订单执行操作,请使用 orderRepository.GetOrdersForCustomer(customerID);进行更改,然后使用 orderRespository.Save(order);

【问题讨论】:

标签: oop design-patterns domain-driven-design ooad grasp


【解决方案1】:

我有几个cmets:

分离分配逻辑是正确的做法

考虑将分配逻辑移动到服务类,例如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 中,因此您无需传递分配列表。

【讨论】:

  • domain models be logic free的优势是什么?域逻辑不应该驻留在域实体中吗?有什么好的参考可以证明不是吗?
  • 好吧,模型将是一个 1-2-1 映射到您的数据库表。然后,您可以创建一个“实体”以使用业务逻辑围绕模型。在这种情况下,服务充当您的域实体。
【解决方案2】:

业务规则也与现有分配相关。让 Allocation 成为 Aggregate 并在其 Factory 中包装业务规则怎么样?喜欢:

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
}

所以在这种情况下,我们不必担心如何找到现有的Allocations。并且可以使用规范模式进一步重构规则验证。

【讨论】:

  • 不会变成anemic domain model吗? OOP 的information expert 方面在这里丢失了,不是吗?参考Services in Domain-Driven Design (DDD)
  • @eulerfx 我已经在这里提到了你的帖子Services in Domain-Driven Design (DDD)?你有不同的建议吗?
  • @Lijo 是责任决定了代码的归属。如果共振不属于任何单个对象或似乎属于多个对象,我个人更喜欢引入一个特定的概念(对象)。
【解决方案3】:

分配逻辑分为两个类 - Project 和 Employee..

我不会这样做,因为它拆分了分配责任,从而违反了单一责任原则。如果您发现它既不属于Project 也不属于Employee,那么domain service 可以完成这项工作。一般而言,涉及不属于同一聚合的多个实体的操作是位于此类服务中的候选对象。

List&lt;Allocation&gt; 作为参数传递给 Allocate 方法,而不是作为类的属性添加...这是正确的方法还是我需要在这两个类中添加 List&lt;Allocation&gt; 作为属性?

我的答案不是那些:仅将 List&lt;Allocation&gt; 添加到您的 Project 课程中。

我认为您需要考虑的是Allocation 在您的域中真正代表什么。它是构成项目聚合一部分的实体吗?它甚至可能是一个值对象而不是一个实体?

当我有数据库关系时,有时我会发现自己失去了对领域的看法。在这种情况下,我看到分配表甚至没有自己的 id;相反,它似乎只代表了ProjectEmployeeRole 之间的关系,具有几个属性。尽管域模型不应该关心持久性,但这可能会暗示Allocation 真正代表什么。

在我看来,Allocation 仅在 Project 的上下文中才有意义,因此它应该是该聚合的一部分。可以说,its equality is not based on identity 因此,它甚至可能是一个值对象。确保满足第一个限制(分配时不超过预算)的责任属于Project 实体,并且可以在员工分配时执行。

棘手的限制是第二个:Employee 通过多个Projects 分配不超过 100%。在这种情况下,您可能有兴趣提供方法来获取分配给定Employee 的那些Projects,可能通过您的Project 存储库。您还可以提供一个操作来检查以提供给定Employee 的总分配,可能通过域服务

请注意,您实际上是在 Project 类的 Allocate 方法中执行所有这些逻辑:首先您通过 GetAllocationsForEmployee 获得所有 Allocations,然后将检索到的列表传递给 Employee.Allocate 这可以实际上被命名为CanBeAllocated。你可能觉得 Employee 有责任确保这个业务逻辑,但我认为它与它的属性和行为都没有关系,因此,它应该是 Project.Allocate 方法的一部分或一个域服务,如果您一直觉得责任混杂。

最后一点,如果之前的 cmets 有一些混淆,将逻辑放在模型类中并没有错,它实际上是整个领域建模的基础部分! AnemicDomainModel post by Martin Fowler 对此提供了一些很好的见解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多