将保存/预存方法放在域对象中的位置?

时间:2011-09-15 10:30:15

标签: oop object domain-driven-design

我想在每次保存域对象时强制执行一些规则,但我不知道实现此目的的最佳方法。正如我所看到的,我有两个选择:向域对象添加save方法,或者在保存到应用程序层之前处理规则。请参阅下面的代码示例:

using System;

namespace Test
{

    public interface IEmployeeDAL
    {
        void Save(Employee employee);
        Employee GetById(int id);
    }

    public class EmployeeDALStub : IEmployeeDAL
    {
        public void Save(Employee employee)
        {

        }

        public Employee GetById(int id)
        {
            return new Employee();
        }
    }

    public interface IPermissionChecker
    {
        bool IsAllowedToSave(string user);
    }

    public class PermissionCheckerStub : IPermissionChecker
    {
        public bool IsAllowedToSave(string user)
        {
            return false;
        }
    }

    public class Employee
    {
        public virtual IEmployeeDAL EmployeeDAL { get; set; }
        public virtual IPermissionChecker PermissionChecker { get; set; }

        public int Id { get; set; }
        public string Name { get; set; }

        public void Save()
        {
            if (PermissionChecker.IsAllowedToSave("the user"))  // Should this be called within EmployeeDAL?
                EmployeeDAL.Save(this);
            else
                throw new Exception("User not permitted to save.");
        }
    }

    public class ApplicationLayerOption1
    {
        public virtual IEmployeeDAL EmployeeDAL { get; set; }
        public virtual IPermissionChecker PermissionChecker { get; set; }

        public ApplicationLayerOption1()
        {
            //set dependencies
            EmployeeDAL = new EmployeeDALStub();
            PermissionChecker = new PermissionCheckerStub();
        }

        public void UnitOfWork()
        {
            Employee employee = EmployeeDAL.GetById(1);

            //set employee dependencies (it doesn't seem correct to set these in the DAL);
            employee.EmployeeDAL = EmployeeDAL;
            employee.PermissionChecker = PermissionChecker;

            //do something with the employee object
            //.....

            employee.Save();
        }
    }

    public class ApplicationLayerOption2
    {
        public virtual IEmployeeDAL EmployeeDAL { get; set; }
        public virtual IPermissionChecker PermissionChecker { get; set; }

        public ApplicationLayerOption2()
        {
            //set dependencies
            EmployeeDAL = new EmployeeDALStub();
            PermissionChecker = new PermissionCheckerStub();
        }

        public void UnitOfWork()
        {
            Employee employee = EmployeeDAL.GetById(1);

            //do something with the employee object
            //.....

            SaveEmployee(employee);
        }

        public void SaveEmployee(Employee employee)
        {
            if (PermissionChecker.IsAllowedToSave("the user"))  // Should this be called within EmployeeDAL?
                EmployeeDAL.Save(employee);
            else
                throw new Exception("User not permitted to save.");
        }
    }
}

在这种情况下你做了什么?

3 个答案:

答案 0 :(得分:0)

我更倾向于第二种方法,即关注点之间存在明显的分离。有一个负责DAL的类,还有一个负责验证,另一个负责编排这些。

在您的第一种方法中,您将DAL和验证注入业务实体。如果我认为向实体注入验证器可能是一个好的做法,那么将DAL注入商业实体绝对不是一个好的实践恕我直言(但我知道这只是一个示范,并且在一个真实的项目中,你会在至少使用服务定位器)。

答案 1 :(得分:0)

如果我必须选择,我会选择第二个选项,以便我的实体不与任何DAL基础设施相关联,并且完全专注于域逻辑。

但是,我真的不喜欢这两种方法。我更喜欢采用更多AOP方法来实现安全性和安全性。通过向我的应用程序服务方法添加属性来实现角色。

我要改变的另一件事就是摆脱'CRUD'的思维模式。如果您能够针对特定命令/用例进行保护,则可以提供更精细的安全选项。例如,我做到了:

public class MyApplicationService
{
    [RequiredCommand(EmployeeCommandNames.MakeEmployeeRedundant)]
    public MakeEmployeeRedundant(MakeEmployeeRedundantCommand command)
    {
        using (IUnitOfWork unitOfWork = UnitOfWorkFactory.Create())
        {
            Employee employee = _employeeRepository.GetById(command.EmployeeId);

            employee.MakeRedundant();

            _employeeRepository.Save();
        }
    }
}

public void AssertUserHasCorrectPermission(string requiredCommandName)
{
    if (!Thread.CurrentPrincipal.IsInRole(requiredCommandName))
        throw new SecurityException(string.Format("User does not have {0} command in their role", requiredCommandName));
}

你拦截对第一个方法的调用,并调用第二个方法传递他们必须拥有的角色。

以下是关于如何使用统一拦截的链接:http://litemedia.info/aop-in-net-with-unity-interception-model

答案 2 :(得分:0)

  

将保存/预存方法放在域对象中的位置?

域对象在DDD中是持久无知的。他们没有意识到有时他们被“冻结”运送到某个存储器然后恢复。他们没有注意到这一点。换句话说,域对象始终处于“有效”且可保存的状态。

权限也应该是持久的 - 无知的,并且基于域和泛在语言,例如:

  

只有来自销售组的用户才能将OrderLines添加到订单中   待定状态

相反:

  

只有销售组的用户才能保存订单

代码可能如下所示:

internal class MyApplication {

    private IUserContext _userContext;
    private ICanCheckPermissions _permissionChecker;

    public void AddOrderLine(Product p, int quantity, Money price, ...) {

     if(!_permissionChecker.IsAllowedToAddOrderLines(_userContext.CurrentUser)) {
         throw new InvalidOperationException(
            "User X is not allowed to add order lines to an existing order");
     }

     // add order lines

    }
}