处理依赖于用户的应用程序



我目前正在编写的应用程序严重依赖于当前登录的用户,举一个具体的例子,假设我们有一个产品列表。

现在,每个用户都有"权利"查看某些产品,该产品的特定详细信息,并编辑/删除其中的更少内容。

例如:

  • 用户可以看到 3/5 个产品
  • 用户可以查看这 3 个产品中的 2 个产品的额外详细信息

由于大多数应用程序的域都是这种情况,因此我倾向于在大多数方法中传递用户。这不时变得繁琐。由于我必须在某些方法中传入用户,只是为了将其传递给另一个需要它的方法。

我的直觉告诉我我错过了一些东西,但我不确定如何解决这个问题。

我给出了一些关于使用保存此用户的类的想法,并将该类注入到我需要它的任何位置。或使用静态属性。

现在不时在方法中方便地传入用户,我想我可以覆盖它:

public doSomething(User user = null)
{
var u = user ?? this.authService.User;
...
}

还有其他方法可以解决此类问题吗?

这将取决于您在项目中的进度。 在某些情况下,您可能没有改变这一点的余地,但如果您有更多的控制权或正在起步,那么您可能有多种选择。

通常,身份和访问控制本身就是一个边界上下文。 身份验证和授权不应位于核心域中。如果您具有访问权限,您的核心域(甚至子域(有兴趣执行它们所做的事情,但确定该访问权限不是域的责任。

授权应在域外部进行。 如果您发现您正在查询您的域,那么事情可能需要改变,因为您需要一个专用的查询层来应用授权。 任何受限的命令都将在集成/应用程序层应用授权。 我们是否要限制用户注册新订单甚至某种类型的新订单并不重要,因为只有粒度会发生变化。

您可能有一个处理特定于您的域的授权的子域和一个更正交的身份和访问控制通用子域。

但是,您可能处于数据元素授权(分类级别(和结构之间存在令人不安的高耦合级别的情况。 我认为流体分类应该远离结构,因为分类变化的影响太大了。

只是一些想法:)

你的直觉是正确的,请继续倾听。

授权检查不应与核心域检查混合使用。例如,检查用户是否可以更新产品详细信息的if和检查产品详细信息是否足够长的if不应包含在同一类甚至相同的边界上下文中。如果你有一个整体,那么这两个检查应该包含在单独的命名空间/模块中。

现在我将告诉你我是怎么做到的。在我最新的整体项目中,我经常使用 CQRS,我喜欢命令和查询之间的分离。我将给出一个命令验证的示例,但这可以扩展到查询验证,甚至扩展到非 CQRS 体系结构。

对于每个命令,我都会注册零个或多个命令验证器,以检查命令是否可以发送到aggregate。这些验证器最终是一致的。如果一个命令通过了所有验证程序,那么该命令将被发送到aggregate,在那里以强一致性的方式进一步检查它。因此,我们谈论的是两种验证:聚合外部的验证和聚合内部的验证。属于其他边界上下文的检查可以使用聚合外部的命令验证器实现,这就是我的做法。现在有一些示例源代码,在PHP中:

<?php
namespace CoreDomain {
class ProductAggregate
{
public function handle(ChangeProductDetails $command):void //no return value
{
//this check is strong consistent
//the method yields zero or more events or exception in case of failure
if (strlen($command->getProductDetails()) < 10) {
throw new Exception("Product details must be at least 10 characters long");
}
yield new ProductDetailsWereChanged($command->getProductId(), $command->getProductDetails());
}
}
}
namespace Authorization {
class UserCanChangeProductDetailsValidator
{
private $authenticationReaderService;
private $productsPermissionsService;
public function validate(ChangeProductDetails $command): void //no return value, if all is good no exception are thrown
{
//this check is eventual consistent
if (!$this->productsPermissionsService->canUserChangeProductDetails($this->authenticationReaderService->getAuthenticatedUserId(), $command->getProductId())) {
throw new Exception("User may not change product details");
}
}
}
}

此示例使用一种样式,其中命令直接发送到聚合,但您也应将此模式应用于其他样式。为简洁起见,不包括命令验证程序注册的详细信息。

相关内容

最新更新