Solve apparent need for outside reference to entity inside aggregate (DDD)

时间:2016-08-31 16:55:29

标签: domain-driven-design

I'm trying to follow DDD principles to create a model for determining whether or not an Identity has access to an Action belonging to a Resource.

A Resource (e.g. a webservice) is something that holds a number of Actions (e.g. methods), which can be accessed or not. An Identity is something that wants to access one or more Actions on a Resource. For example, someone uses an api-key in a call to a webservice method, and it must be determined whether or not access is allowed.

As I currently see it, Identity and Resource are aggregate roots, and Action is an entity belonging to Resource. It doesn't seem to make sense for an Action to live on its own; it will always belong to one Resource. An Identity needs to know to which Resource Actions it has access. This seems to suggest the following model.

Model

However, as I understand it, this violates the principle that something outside an aggregate cannot reference an entity within the aggregate. It must go through the root. Then I'm thinking, what if Action was the aggregate root and Resource an entity? But that doesn't seem very logical to me. I've also been thinking of merging Resource and Action into one entity, which would then be an aggregate root, but that also seems wrong to me.

So it leaves me kind of stuck on how to model this correctly using DDD principles. Anyone have a good idea on how to model this?

Update: The model I'm trying to create is the identity model for defining which resource actions an Identity is allowed to access. It is not a model for the actual implementation of resources and actions.

Update 2 - invariants: Id of all objects is given at birth, is unique, and doesn't change. ApiKey of Identity must be unique across all Identities. Name of Action must be unique within aggregate, but two different Resources can have Actions with same names, e.g. Resource "R1" can have an Action "A1" and Resource "R2" can also have an Action "A1", but the two "A1"s are not the same.

4 个答案:

答案 0 :(得分:1)

查询或写入操作?

在聚合和实体方面的域模型在DDD中具有其目的,以简化不变量的表达和实施 - 因为写操作应用于模型。

正如@ VoiceOfUnreason的回答中提到的那样,问题是“这个用户可以在资源R'上执行操作A.是一个不一定需要流经域模型的问题 - 它可以通过查询预先计划的只读模型或标准SQL查询构成写模型持久性的表来回答(取决于你的需要)。

拆分上下文以简化不变量

但是,您的问题虽然主要是关于如何识别身份是否被允许执行某个操作,但却隐含地寻求更简单的模型来更新资源,操作和权限。所以要探索这个想法......隐含地有两种类型的写操作:

  1. 定义可用资源和操作
  2. 定义允许执行特定身份的资源操作组合
  3. 如果这两种类型的操作的模型被分成不同的有界上下文,那么它们的模型可能会被简化。

    在第一个模型中,您将按照自己的模型进行建模,将Aggregate with Resource作为聚合根,将Action作为包含的实体。这允许强制执行动作名称在资源中必须唯一的不变量。

    在此上下文中进行更改时,您可以发布事件,例如ActionAddedToResourceActionRemovedFromResource

    在第二种情况下,您有三个聚合:

    • 身份
    • ResourceAction
      • 属性:Id,ResourceId,ResourceName,ActionId,ActionName
    • 权限

    将根据在ActionRemovedFromResource上删除的ActionAddedToResource上创建的第一个上下文发布的事件更新ResourceAction实例。如果没有动作的资源,则根本没有ResourceAction。

    权限将包含两个身份引用 - IdentityIdResourceActionId

    这种方式在执行操作时#34;允许此用户对此资源执行此操作"该操作只是为了创建一个新的Permission实例 - 减少影响Identity聚合的一致性边界的操作集 - 假设没有不变量需要“权限”的概念。在Identity聚合中强制执行?

    这也简化了事情的查询方面,因为您只需要在identityId加入{resourceName之后搜索匹配actionNamePermissionsResourceActions的权限条目{1}}。

    责任层

    战略设计部分中的DDD书是指根据责任层组织您的上下文。要使用本书中的术语,上述建议是基于“能力”的概念。责任层(定义资源和行动)和“可操作”。责任层(定义身份权限和检查身份权限)。

答案 1 :(得分:0)

根据您提到的不变量,Identity可以包含资源字典/映射,其中resourceId是键,值是一组唯一的操作名称/ ID。这为每个标识提供了每个资源的操作名称的唯一性:

Map<Resource, Set<Action>>

或者,您可以拥有一组资源列表,并且它们上面有一系列操作。可以通过您编码的语言中提供的集合类型强制实现唯一性:

Set<Resource> Resources

class Resource {
    Set<Action> Actions
}

更简单,只需通过组合两个ID创建一个Resource-Action键,并将其存储在一个集合中,或者为您提供唯一性:

Resource1-Action1

Resource1-Action2

Resource2-Action1

...等

然后,您可以在Identity上添加一个方法来添加新的资源 - 操作组合。

我不会在您的说明中看到任何内容,因为行为是实体,因为它们似乎没有自己的身份。

这很简单,所以我假设你已经大大简化了这个领域。

答案 2 :(得分:0)

  

例如,有人在调用webservice方法时使用api-key,必须确定是否允许访问。

这是一个查询。从根本上说,通过加入属于不同聚合的实体的两个只读副本来回答查询没有任何问题。

您需要注意,因为聚合可以彼此独立地更改,并且因为它们可以独立于您的查询而更改,所以您在进行连接时获得的答案可能是陈旧的,并且不完全一致。 / p>

例如,您可能会将100ms之前写入的Identity的副本加入到200ms前写入的Action的副本中。在运行查询时,任何一个聚合都可能会发生变化。

答案 3 :(得分:0)

我还将扩展由@VoiceOfUnreason识别的位:

  

例如,有人在调用webservice方法时使用api-key,   必须确定是否允许访问。

暴露功能的特定位如何知道应用了哪些安全性?答案由@Chris Simon提供:Permission

我有一个常用的实现,我使用它并没有被提炼到它自己的Identity & Access BC,但是跟你正在尝试的东西密切相关 - 我希望:)

Session有一个Permission字符串列表。通常我使用uri来表示权限,因为它非常易读。像my-system:\\users\list这样的东西。无论如何,如何为用户分配这些权限可能是任何事情。可能有一个Role包含权限,并且用户被分配给一个或多个角色;或者用户甚至可以拥有自定义权限列表。

当请求SessionToken时(通过身份验证),服务器会检索相关用户的权限,并创建一个具有分配给它的相关权限的会话。这会产生一个读取端token/permission

为每个公开的功能位(例如休息端点)分配了权限。在c#web-api中,它只是该方法的一个属性:

[RequiredPermission("my-system:\\order\create")]

我的会话令牌在标头中传递,并且快速检查确定会话是否已过期以及会话(分配给用户)是否有权访问该资源。

使用您的设计,Action可能会带有所需的PermissionUser仍然需要包含UserActionResourceId的角色或ActionId条目的列表。当使用该结构创建读取优化会话结构中的使用日志时。

如果有任意Action列表可以分配给任何Resource,则ResourceAction可能都是聚合。然后你需要@Chris Simon提到的ResourceAction。然后,ResourceAction将包含Permission

这是我对它的看法......