在为新用户选择角色时,应用程序或业务/域层负责

时间:2016-03-31 19:44:54

标签: c# architecture domain-driven-design encapsulation

我很难理解哪个层(应用程序与域)应该负责为新用户添加角色。例如,如果创建了管理员,则应该接收管理员角色。我可以看到两种方法

应用层责任:

public class AddBasicUserCommand
{
    public User Handle(string userName, string role)
    {
        var user = new User
        {
            UserName = userName,
            Role = role
        };
        //... persist user to storage
    }
}

域层责任:

  public class AddBasicUserCommand
{
    public User Handle(string userName)
    {
        var user = new User
        {
            UserName = userName,
            Role = "basicrole"
        };
        //... persist user to storage
    }
}

我将其推向应用层的问题是,当有多个客户端/应用程序可以创建用户时,他们都必须就如何描述不同的角色达成一致,例如管理员角色由“admin”描述。例如,这可能发生:

public class UserController
{
    public IActionResult CreateBasicUser(string userName)
    {
        //this happens at application 1
        var user = new AddBasicUserCommand().Handle(userName, "basicrole");

        //this happens at application 2
        var user = new AddBasicUserCommand().Handle(userName, "badrole");
        return View(user);
    }
}

当这两个客户端/应用程序都在同一个数据库上工作时,可以使用错误的角色轻松创建用户。

在创建每种类型的用户时,每个客户端都需要测试是否传递了正确的角色。 如果我们将这个责任移出客户端,客户端就会变得更容错:

public class UserController
{
    public IActionResult CreateBasicUser(string userName)
    {
        //this happens at application 1
        var user = new AddBasicUserCommand().Handle(userName);

        //this happens at application 2 - no chance of passing bad role value
        var user = new AddBasicUserCommand().Handle(userName);
        return View(user);
    }
}

我的问题是,这个问题应该在域方处理,还是授权逻辑的这部分,应该在客户端/应用程序端处理?

2 个答案:

答案 0 :(得分:1)

  

这个问题在域方面处理,或者是这部分   授权逻辑,应该在客户端/应用程序上处理   侧?

如果您愿意,我会同意:安全是域还是基础设施问题?

在我看来,这两种方法仍然可以很好地分离关注点。

在继续我的理由之前,我会说你应该明确地将其作为域代码实现,因为它不仅更多 DDD-ish ,而且还提高了整个域的所有消费者的可重用性和一致性:有一种向用户添加角色的方法。

无论如何,我会建议你采用一种混合但功能强大的方法。

首先,将您的安全层实施为,就像定期执行一样。提供安全服务,轻松使用整个安全域。

其次,在您的基础架构代码中,设计一些这样的外观界面:

public interface ISecurityFacade
{
     void SetUserRole(string userName, string roleName);
}

...并实施它以使用您的安全域。

此方法的主要优点是其他域可以与您的安全域协作,而即使是非域代码也可以与您的安全层协同工作。

您甚至可以使用依赖注入和控制反转将其提升到新的水平,当您需要在任何域外使用安全层时,您可以注入ISecurityFacade,而如果某些域需要使用安全性,您可能需要注入ISecurityService

也许你迷失了ISecurityServiceISecurityFacade的差异。

ISecurityService可以在事务中启动或协作,而ISecurityFacade实现原子方法:每个方法代表一个完整的事务。

外观的全部意义在于消费者不知道其实施是使用域名还是谁知道什么:他们盲目地依赖于外观。在您的特定情况下,您将提供将使用该域的实现。例如:DomainSecurityFacade

关于使用命令模式......

这只是我的观点,但如果您想利用DDD,那会感觉很难看。您的应用程序层应使用服务或外观......您不需要命令。

例如,这不是更具可读性和可理解性吗?

// Before
var user = new AddBasicUserCommand().Handle(userName, "basicrole");

// After
var user = SecurityFacade.SetRoleToUser(userName, "basicRole");
顺便说一下,这只是我的意见......

OP在一些评论中问道......

  

如果我理解正确的话,你会创建一个立面但是离开了   为新用户选择正确角色的责任   应用?这不会给这个问题留下太多的责任   域/业务层的用户?

不,将角色设置为用户域对象的责任属于安全域。

显然,在某些时候,应用程序层可能会通过应用程序服务使用域服务,并且它会尝试为某个给定用户设置角色。

您可能会认为设置角色的责任似乎是应用层的成员,但是该层不能直接与域一起使用,只是触发器启动了域名交易。

在某些时候,也许在您的用户存储库中,您应该实现某种域验证。我将使用规范模式,并且一些UserRoleAssignmentSpec应该验证为要避免数据损坏而存在要更新的用户的角色。

由于谁实际修改域是域本身,并且域还可以验证自身以保留它以使其工作,谁最终负责处理安全性? ;)

答案 1 :(得分:0)

您可以做的是将Authorization视为子域名并为其提供自己的有界上下文。客户端都将通过此BC来添加和获取用户角色。

您可以选择使用DDD战术模式实施Authorization BC - 如果您这样做,它将拥有自己的应用程序,域和基础架构层。 由于角色管理通常不是域逻辑的复杂,您还可以选择比DDD更轻量级的东西,例如具有快速应用程序脚手架的CRUD框架或事务脚本。部分或全部有界上下文也可以包含现成的授权/访问控制解决方案。

如何利用其他有界上下文中的授权来控制对您域的访问权限,可能更棘手。如果您希望保持简单,通常在应用程序级别执行此操作,在执行操作之前检查用户是否具有足够的角色。应用程序服务/命令处理程序如何获得用户的角色可以采用具有各种便利性和间接性的多种形式,从使用由底层技术提供的某种安全上下文到“手动”调用授权BC。在幕后,必须以某种方式联系授权BC,例如,如果我们正在进行中,则直接调用授权模块,或者如果在进程外,则向REST服务发送消息。