将保存/预保存方法放在域对象中的哪里?

发布于 2024-12-04 17:27:59 字数 3045 浏览 3 评论 0原文

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

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.");
        }
    }
}

在这种情况下您会做什么?

I want to enforce some rules every time a domain object is saved but i don't know the best way to achieve this. As, i see it, i have two options: add a save method to the domain object, or handle the rules before saving in the application layer. See code sample below:

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.");
        }
    }
}

What do you do in this situation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

翻了热茶 2024-12-11 17:27:59

我更喜欢第二种方法,其中关注点之间有明确的分离。有一个类负责 DAL,另一个类负责验证,还有一个类负责编排这些。

在第一种方法中,您将 DAL 和验证注入到业务实体中。我可能会争论,如果将验证器注入到实体中可能是一种好的做法,那么将 DAL 注入到业务实体中绝对不是一种好的做法,恕我直言(但我知道这只是一个演示,在实际项目中,您会在至少为此使用服务定位器)。

I would prefer the second approach where there's a clear separation between concerns. There's a class responsible for the DAL, there's another one responsible for validation and yet another one for orchestrating these.

In your first approach you inject the DAL and the validation into the business entity. Where I could argue if injecting a validator into the entity could be a good practice, injecting the DAL into the business entity is is definitely not a good practive IMHO (but I understand that this is only a demonstration and in a real project you would at least use a service locator for this).

忆依然 2024-12-11 17:27:59

如果我必须选择,我会选择第二个选项,这样我的实体就不会与任何 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));
}

您将拦截对第一个方法的调用并调用第二个方法,传递它们在其角色中必须具有的东西。

以下是有关如何使用 unity 进行拦截的链接:http://litemedia.信息/aop-in-net-with-unity-interception-model

If I had to choose, I'd choose the second option so that my entities were not associated to any DAL infrastructure and purely focused on the domain logic.

However, I don't really like either approach. I prefer taking more of an AOP approach to security & roles by adding attributes to my application service methods.

The other thing I'd change is moving away from the 'CRUD' mindset. You can provide much granular security options if you secure against specific commands/use cases. For example, I'd make it:

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));
}

Where you'd intercept the call to the first method and invoke the second method passing the thing that they must have in their role.

Here's a link on how to use unity for intercepting: http://litemedia.info/aop-in-net-with-unity-interception-model

西瑶 2024-12-11 17:27:59

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

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

权限也应该是持久无知的,并且基于领域和通用语言,例如:

只有销售组的用户才能将 OrderLine 添加到订单中的订单
待处理状态

与以下情况相反:

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

代码可以如下所示:

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

    }
}

Where to put the save/pre save methods in a domain object?

Domain objects are persistent-ignorant in DDD. They are unaware of the fact that sometimes they get 'frozen' transported to some storage and then restored. They do not notice that. In other words, domain objects are always in a 'valid' and savable state.

Permission should also be persistent-ignorant and based on domain and Ubiquitous Language, for example:

Only users from Sales group can add OrderLines to an Order in a
Pending state

As opposed to:

Only users from Sales group can save Order.

The code can look like this:

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

    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文