DDD - 如何强制不变量但具体到客户要求?

问题描述 投票:0回答:4

我正在尝试找出如何使项目的一些消费者(业务客户)保持不变,这些消费者对同一版本的聚合根有自己的要求。 让我们以客户为例,提出假设性问题来满足以下愚蠢的逻辑:

public class Customer 
{
  public Id { get; private set;}
  public string Name { get; private set;}

  public void SetName(string name){
     //client1 -> requires the name not to be null
     //client2 -> requires the name can start with "J"
     //client3 -> some other business logic
     this.Name = name;
  }
}

目前,我想到的是自定义验证/不变检查策略逻辑,如下所示:

public void SetName(string name, INameCheckStrategy strategy){
    if(!strategy.IsSatisfiedBy(name)) throw new BusinessException("name does not meet the invariant check!");
     this.Name = name;
  }

哪里

public class Client1NameCheckStrategy : INameCheckStrategy {
   public bool IsSatisfiedBy(string name){
     return name != null;
   } 
}

有什么想法如何处理这样的问题吗?

c# domain-driven-design encapsulation strategy-pattern invariants
4个回答
1
投票

您可以采用某种 DDDD(“动态域驱动设计”,创造一个短语)方法,将域中的规则具体化为它们自己的聚合,并将它们与客户相关联。 如果您要对更改规则进行建模,这可能会让事情变得有趣(您如何处理使聚合被先前规则视为有效的更改?)。


0
投票

如果领域模型实体在数据方面保持相同,并且数据对于所有业务客户具有相同的含义,那么使用类似策略模式(如您所建议的)之类的东西似乎很合适。只需确保您不会让配置基础设施等任何内容泄漏到您的域模型中,并通过注入所需的信息和客户端特定逻辑来严格执行。

如果聚合中有多个位置应用此特定逻辑,您还可以考虑让存储库(或工厂)在从存储库集合查询聚合时注入策略。

另一种选择是使用值对象的特定实现,无论如何它应该已经包含其数据的业务不变量。在您编造的示例中,可能存在不同类型的 CustomerName 值对象(例如 CustomerNameClientX)。根据当前客户是谁,您可以确保创建相应的客户名称值对象,在创建过程中已验证自身并传递给聚合。


0
投票

我知道你问过 C#,甚至提到了 DDD,但如果我被要求真正帮助你,我会给你一个 JS 的例子。

function setName(name) {
    this.name = name;
}

没有类型检查,也不需要它们,因为在真正需要更多安全之前限制自己是一个坏习惯。

看,我只是使用了这个属性而没有检查它的类型,并且涵盖了我所有的业务案例:

function insertToMongoByName(db, collectionName, record) {
    console.log(`Saving record by name: ${record.name}`);
    record._id = record.name;        
    db.collection(collectionName).insertOne(record);
}

Mongodb 需要

_id
存在,所以我满足了这个不变量。

console.log
可以与任何东西一起工作,就像 JS 中的许多其他库一样(另请参阅我使用过的
insertOne
函数)。

这是该名称的另一种用法:

_ = require("lodash");

function getEmailWithDisplayName(customer) {
    if(customer.fullEmail) {
        return customer.fullEmail;
    }

    if(!customer.email) {
        return undefined;
    }

    if(_.isString(customer.name)) {
        return `${customer.name} <${customer.email}>`;
    }
    else if(_.isObject(customer.name) && (customer.name.firstName || customer.name.lastName)) {
        const name = [customer.name.honorific, customer.name.firstName, customer.name.lastName]
            .filter(s => _.isString(s)).join(" ");
        return `${name} <${customer.email}>`;
    }

    return customer.email;
}

这次我必须围绕

name
进行一些类型检查,但这对于我想要实现的目标非常具体。

在此之后,我对您的架构的问题是:您真的想修复您的

Customer
类以支持所有可能的未来需求吗?所有未来的用例都会乐意提供检查“策略”,而不是按照他们的想法前进吗?


0
投票

我知道这是一个老问题,但在这里为其他可能过来的人留下答案:

这可能是规范模式的一个很好的例子,并存储“IsCustomerValid”规范的“客户端特定”配置。

类似:


var specification = _validationFactory.CustomerSpecification(clientId);
if(!specification.IsSafisfiedBy(customer))
    throw CustomerDataInvalidException("...");

如果您想发挥创意,您可以对规范模式进行调味以返回结果类型而不仅仅是布尔值,这样您就可以返回一条有意义的错误消息,说明输入如何不适合特定的规范实例。

© www.soinside.com 2019 - 2024. All rights reserved.