业务规则分为两类

时间:2013-12-23 08:12:36

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

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

  1. 当新员工被分配到项目时,总支出不应超过预算金额。
  2. 对于员工,总分配百分比不应超过100%
  3. 我创建了如下C#中创建的实体。

    问题

    Allocate逻辑分为两个类 - Project和Employee .. List<Allocation>作为参数传递给Allocate方法,而不是作为类的属性添加......是否正确方法还是我需要在这两个类中添加List<Allocation>作为属性?

    注意:

    数据库

    enter image description here

    授权

    enter image description here

    代码

    项目

     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

      

    有什么行为要求客户拥有订单清单?当您更多地考虑域的行为(即在什么点需要什么数据)时,您可以根据用例对事物进行建模,事情变得更加清晰和简单,因为您只需更改跟踪一小组对象在总边界。

         

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

3 个答案:

答案 0 :(得分:2)

我有几点意见:

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

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

答案 1 :(得分:1)

业务规则也与现有分配相关。如何使Allocation成为聚合并在其工厂中包装业务规则?像:

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
}

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

答案 2 :(得分:1)

  

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

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

  

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

我的回答不是那些:仅将List<Allocation>添加到您的Project课程。

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

当我有数据库关系时,有时我发现自己失去了对域的看法。在这种情况下,我看到Allocation表甚至没有自己的id;相反,它似乎只代表具有多个属性的ProjectEmployeeRole之间的关系。虽然域模型不应该关注持久性,但这可能会提供一些关于Allocation真正代表什么的提示。

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

棘手的约束是第二个:Employee通过几个Projects分配不超过100%。在这种情况下,您可能有兴趣提供方法来获取分配了给定Projects的{​​{1}},可能通过您的Employee 存储库。您还可以提供检查操作,以便通过域名服务提供给定Project的总分配。

请注意,您实际上是在Employee课程的Allocate方法中执行所有这些逻辑:首先,您获得ProjectAllocations的全部内容然后通过检索到GetAllocationsForEmployee的列表,该列表实际上可以命名为Employee.Allocate。您可能认为CanBeAllocated负责确保此业务逻辑,但我认为它与其属性及其行为无关,因此,它应该是{{1}的一部分。方法或域名服务,如果您一直认为存在混合责任。

最后要注意的是,如果先前的注释存在一些混淆,将逻辑放入模型类中是没有错的,它实际上是整个域建模的基本部分! AnemicDomainModel post by Martin Fowler提供了一些很好的见解。