DDD:如何从Kotlin中的集成层隐藏特定的聚合根构造函数

时间:2018-03-27 20:11:31

标签: java kotlin domain-driven-design

我对DDD有点新意,甚至在阅读了蓝皮书和红皮书之后,我仍然有一些关于如何将一些原则转换为代码的问题,特别是使用Kotlin和Java。

例如,我确定了一个客户端聚合根,它接收了一些创建所需的参数,如名称和地址:

class Client: AggregateRoot {

    var clientId: ClienteId
    var name: Name
    var address: Address

    constructor(name: Name,address: Address) : super(){
        // validations ....
        this.name = name
        this.address = address
    }

简单部分: 要创建一个新客户端,我在RS服务中接收一个DTO,并尝试创建一个传递上述参数的新Client类,大小写一切都很可靠,所有规则都已完成我将Client的新实例发送到存储库,非常直接。 / p>

clientRepository.store(client)

其他部分: 我需要搜索我的客户端以更改地址,因此我将id发送到存储库并在数据库中找到Client然后我需要将数据库实体转换为聚合根并返回给调用者。

override fun getById(id: Long): Client {
  val clientEntity = em.find(...)
  val client: Client(.....) //But I need another constructor with ClientId
  return client
}

然后我需要一个新的构造函数,它接收更多参数,如ClientId

constructor(clientId: ClienteId,name: Name,address: Address) : super(){

问题是每个服务都可以调用这个新构造函数并创建一个不正确的聚合根实例,所以我的问题是:

  1. 有没有办法隐藏整个构造函数,只是为了查看存储库或特定层。就像在C#中你可以使用内部。
  2. Java或Kotlin是否有任何解决方案不公开应该仅用于测试和集成的构造函数?
  3. 另一个例子是,如果我不是每次创建客户端时都需要传递地址,而是在另一种方法之后传递地址:

    client.addAddress(address)
    

    但在这两种情况下,我都需要从数据库中完成整个Client,所以我需要第二个带有address参数的构造函数。

1 个答案:

答案 0 :(得分:3)

因此,问题是如何通过将错误的接口暴露给客户端代码(即应用层或表示层)来从持久性中重新合并聚合而不破坏其封装。

我看到两个解决方案:

  1. 使用反射填充字段。这是大多数ORM使用的解决方案,也是最通用的。它适用于大多数持久性类型,即使存在阻抗不匹配。一些ORM需要注释字段或关系。

  2. 向客户端代码公开不同的接口。这意味着您的Aggregate实现更大该接口并包含仅由基础结构使用的其他初始化方法。

  3. 作为伪代码中的一个例子,您可以:

    // what you want the upper layers to see
    interface Client {
        void addAddress(address);
    }
    
    // the actual implementations
    public class ClientAggregate implements Client 
    {
        void rehidrate(clientId,name,address){...}
        void addAddress(address){...}
    }
    
    public class ClientRepository
    {
        // this method returns Client (interface)
        Client getById(id){
            val clientEntity = em.find(...)
            val client = new ClientAggregate()
            client.rehydrate(clientEntity.id, clientEntity.name, clientEntity.address)
            return client //you are returning ClientAggregate but the other see only Client (interface)
        }
    }
    

    作为旁注,我不公开构造函数以从Domain的角度创建聚合。我喜欢使用空构造函数和一个专用方法,以无处不在的语言命名,创建聚合。原因是不清楚构造函数是否创建了新的Aggregate。构造函数实例化一个类的新实例;它更像是一个实现细节而不是域关注。一个例子:

    class Client {
        constructor(){ //some internal initializations, if needed }
        void register(name){ ... }
    }