domain-driven-design - DDD 聚合和实体中的 InversionOfControl(依赖注入(inject))

标签 domain-driven-design inversion-of-control

我的问题是关于你们在实践中将域或基础设施服务注入(inject)到 DDD 聚合中的做法。我确信它在 一般在 DomainObjects 中允许 DependencyInjection 是一个坏主意,因为根据我的经验,它鼓励轻率的开发人员用它做讨厌的事情。然而,在某些特殊情况下,Domainobjects 中的 DependencyInjection 可能是有意义的,尤其是当它有利于可读性和简单性时。
就我而言,我正在尝试解决如何创建新 Ids 的问题。的 aggregateroot .

假设我们有一个 UserAddresses具有 Address 列表的聚合实体作为其聚合根。让我们进一步假设我们有一个方法 changeAddress(AddressId addressId, AddressChangeDto dto)总而言之:

public AddressId changeAddress(AddressId addressId, AddressChangeDto dto) {
    Address address = nullableAddress(addressId);

    if (address == null) {
        // Doesn't matter for this question
    } else if (address.hasChanged(dto)) {
        address = changeAddress(address, dto);            
    }
}

和私有(private) changeAddress(Address address, AddressChangeDto dto)方法简化如下:
private Address changeAddress(Address address, AddressChangeDto dto) {
    if (!addressCopyNeeded(address)) {
        address.change(dto);
        return address;
    }

    // Address is both Shipping- & BillingAddress, hence it must be copied to 2 separate
    // entities.
    Address copiedAddress = address.copy(new AddressId()); // <-- New AddressId created
    ...
}

现在我想重构 AddressIdAddressIdFactory 创建DomainService驻留在我的 DomainModel 中,由驻留在基础设施中的 MongoDbRepository 支持,以创建性能优化 ObjectId来自 MongoDb,我更喜欢长 UUID。

可以通过将该依赖项添加为 来轻松解决该问题。形参像这样:
public AddressId changeAddress(AddressId addressId, AddressChangeDto dto, AddressIdFactory idFactory) {
    Address address = nullableAddress(addressId);

    if (address == null) {
        // Doesn't matter for this question
    } else if (address.hasChanged(dto)) {
        address = changeAddress(address, dto, idFactory);            
    }
}

private Address changeAddress(Address address, AddressChangeDto dto, AddressIdFactory idFactory) {
    if (!addressCopyNeeded(address)) {
        address.change(dto);
        return address;
    }

    // Address is both Shipping- & BillingAddress, hence it must be copied to 2 separate
    // entities.
    Address copiedAddress = address.copy(idFactory.nextAddressId());
    ...
}

但是,我真的一点也不喜欢这种设计,因为它显然让使用这种方法的客户一见钟情:“为什么我想更改现有地址时必须通过 AddressIdFactory?”
客户端没有,并且 不应该有知道内部发生了什么,并且有可能必须在某个星座中创建一个新地址。这也引出了我的下一个论点,我们还可以重构您可以说的整个方法,但这总是会导致客户端负责传入新的 AddressId 或传入 xyz DomainService 作为方法参数的设计,这我认为根本不是最佳的。

因此,我正在考虑将依赖注入(inject)作为一个异常(exception)情况,以便能够保持“何时”和“如何”的逻辑来创建一个新地址,以简化聚合的客户.

问题是如何 .是的,我们使用的是 Spring,不,我不能只使用 Autowiring DomainService,因为整个聚合是一个持久的 Spring Data MongoDb 对象,其中 DomainObject 在运行时从 JSON 反序列化。
@Aggregate
@Document(collection = "useraddresses)
public class UserAddresses extends Entity {

    // When reading from MongoDb the object's creation lays in the responsibility of 
    // Spring Data MongoDb, not Spring, therefore Spring cannot inject the dependency 
    // at all.         
    @Autowired
    private AddressIdFactory addressIdFactory;

    @PersistenceConstructor
    public UserAddresses(String id, Map<String, Address> userAddresses) {
    setUserAddressesId(new UserAddressesId(id));

    if (userAddresses != null) {
        this.userAddresses.putAll(userAddresses);
    }
}

当然,我可以通过调用 setter() 或其他方法在存储库中手动注入(inject)该依赖项,但这也很丑陋,因为我只会出于技术考虑的目的在我的“美丽”聚合上拥有一个公共(public) setter。

另一个想法是使用反射将依赖项直接设置为私有(private)字段,但是我猜这也很丑陋并且有轻微的性能缺陷。

我想知道你的方法会是什么样子?

最佳答案

So the solution to that is "Inversion of Control", which is really just a pretentious way of saying "taking an argument".



我不怪你根本不喜欢这个设计。确实感觉模型的实现细节正在泄露给应用程序。

谜语的部分答案是:您可以通过接口(interface)将应用程序与实现的细节隔离开来。
interface UserAddresses {
    AddressId changeAddress(AddressId addressId, AddressChangeDto dto);
}

因此,当应用程序从存储库加载“聚合”时,它会得到一个实现此接口(interface)的东西。
{
    UserAddresses root = repository.get(...)
    root.changeAddress(addressId, dto)
}

但不一定是应用程序直接与聚合的“根实体”对话。应用程序可能正在与适配器通信。
Adapter::changeAddress(AddressId addressId, AddressChangeDto dto) {
    this.target.changeAddress(addressId, dto, this.addressFactory);
}

同样,应用程序正在与之通信的“存储库”是存储库周围的适配器,用于访问数据存储,并具有将地址工厂注入(inject)适配器的附加管道。

关于domain-driven-design - DDD 聚合和实体中的 InversionOfControl(依赖注入(inject)),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47222414/

相关文章:

.net - ResolveAll 做什么

domain-driven-design - 简单的聚合根和存储库

node.js - "Failed to get configuration"通过 Node 在 Mocha 测试中使用 Wolkenkit 客户端

architecture - 使用 CQRS 进行多对多关系的替代方案

asp.net-web-api - 字符串类型不能构造

.net - 温莎城堡 : How do you add a call to a factory facility not in xml?

c# - 遵循 DDD 概念设计存储库

tdd - 在数据库而不是假存储库上运行测试是不好的做法吗?

testing - 小巧玲珑。 IoC、测试和 Agatha

asp.net - 如何将 CaSTLe Windsor 与 ASP.Net Web 表单一起使用?