【问题标题】:Persistent Model to Domain Model mapping without exposing domains object attributes不暴露域对象属性的持久模型到域模型映射
【发布时间】:2017-10-11 08:49:52
【问题描述】:

我知道这是一个常见问题,但我还没有找到另一个可以解决我疑惑的问题。

通常,如果项目很小,我会在表示域对象的同一对象中添加持久性注释。这允许从数据库加载实体并保持所有设置器私有,确保任何实例始终处于有效状态。比如:

@Entity
class SomeEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String attribute1;
    private String attribute2;
    private String attribute3;
    // ... other attributes

    protected SomeEntity() {}

    /* Public getters */
    public Long getId() { ... }

    public String getAttribute1() { ... }

    public String getAttribute2() {  ... }

    /* Expose some behaviour */
    public void updateAttributes(String attribute1, String attribute2) { 
       /* do some validations before updating */
    }
}

如果我想拥有不同的持久模型,就会出现我的问题。然后我会有类似的东西:

/* SomeEntity without persistent info */
class SomeEntity {
    private Long id;
    private String attribute1;
    private String attribute2;
    private String attribute3;
    // ... other attributes

    protected SomeEntity() {}

    /* Public getters */
    public Long getId() { ... }

    public String getAttribute1() { ... }

    public String getAttribute2() {  ... }

    /* Expose some behaviour */
    public void updateAttributes(String attribute1, String attribute2) { 
       /* do some validations before updating */
    }
}

和 DAO:

@Entity
class SomeEntityDAO {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String attribute1;
    private String attribute2;
    private String attribute3;

    public SomeEntityDAO() {}

    /* All getters and setters */
}

我的问题是,如何在不暴露 SomeEntity 属性的情况下将 SomeEntityDAO 映射到 SomeEntity?

如果我创建一个类似 public SomeEntity(String attribute1, String attribute2, ...) {} 的构造函数,那么任何人都可以创建 SomeEntity 的无效实例。如果我在 SomeEntity 中公开所有设置器,也会发生同样的情况。

我也不认为使用updateAttributes() 构建对象是一个有效的解决方案,因为这将执行一些我此时不想执行的验证(我们信任数据库中持久化的数据)。

我正在考虑保护所有设置器,因此 DAO 可以扩展实体并可以访问设置器...但我不确定这是否是一个好的选择。

解决此问题的最佳或常用方法是什么?

【问题讨论】:

  • 您想要一个允许没有设置器或具有私有设置器的实体类的 ORM?
  • 我的意思是域模型类没有暴露公共设置器。只公开行为方法。吸气剂很好。并且 ORM 类可以有 getter 和 getter。

标签: java oop domain-driven-design persistence ddd-repositories


【解决方案1】:

我也遇到过同样的问题。环顾四周,我没有找到解决方案。相信我,如果它存在,它就隐藏在某个地方。当您必须处理一个 ORM 实体无处不在并且在域和 ORM 模型之间迈出一大步的旧项目时,没有人建议您该做什么。

鉴于此,我推断如果您真的想保持您的域实体纯粹(所以不可获取和设置 - 后者我永远不会接受!)您必须做一些交易。因为如果不给实体一些额外的知识,就无法共享内部结构。请注意,这并不意味着您必须让域实体了解 ORM 层,也不意味着您必须使用 getter。只是,我已经得出结论,域实体应该有办法将它们公开为不同的模型。

因此,总而言之,在您的情况下,我会做的是建立访问者模式。域实体 EntityA 将实现 EntityAVisitable 接口以接受 EntityAVisitor 或类似的东西。

interface EntityAVisitable {
   accepts(EntityAVisitor visitor);
}

构建器实现了访问者所需的接口,EntityAVIsitor

interface EntityAVisitor<T>{
    setCombinedValue1_2(String attribute1_attribute2_combinedInEntity);
    <T> build();
}

EntityAVIsitor接口的 build()函数使用了一个泛型类型T。这样,Domain实体对具体实现的返回类型是不可知的。 EntityAVIsitor

完美吗?没有。

完美的解决方案是摆脱 ORM(实际上我会说我讨厌它们,因为大多数时候使用的方式是错误的 - 但这是我个人的想法)。

好看吗?没有。

由于语言限制(我想您使用 Java),不允许使用好的解决方案。

在封装您的域实体的真实内容方面做得很好吗?是的。

不仅如此,您还可以通过这种方式准确地决定可以公开哪些内容以及如何公开。因此,在我看来,在保持实体纯粹和必须在座位下使用 ORM 之间是一个很好的选择。

【讨论】:

    【解决方案2】:

    域实体应该是自我验证的,这意味着它应该只根据其内部值来验证自己。如果更新需要依赖于外部依赖项的验证,那么我将创建一个负责更新的更新程序类。在更新程序类中,您可以使用规范模式(作为可注入依赖项)来实现验证。

    修改时使用域实体,只读投影使用 DTO。当您以只读方式使用直接 DTO 时,会获得性能和简化方面的收益。这用于 CQRS 模式。

    class SomeEntity {
        private Long id;
        private String attribute1;
        private String attribute2;
        private String attribute3;
        // ... other attributes
    
        public SomeEntity() {}
    
        /* Public getters/setter */
        public Long getId() { ... }
    
        public String getAttribute1() { ... }
    
        public String getAttribute2() {  ... }
    
        public Long setId() { ... }
    
        public String setAttribute1() { ... }
    
        public String setAttribute2() {  ... }
    }
    
    //classes/interfaces named for clarity
    class EntityUpdater implements IEntityUpdater {
      public EntityUpdater (ISpecification spec){
      }
    
      public updateEntity(SomeEntity entity){
        //assert/execute validation
      }
    }
    

    【讨论】:

      【解决方案3】:

      一些 ORM 允许通过字段访问设置实体值(与 setter 方法相反)。

      JPA 使用@Access 注释。见What is the purpose of AccessType.FIELD, AccessType.PROPERTY and @Access

      我创建了一个可以使用字段访问的 ORM,sormula。请参阅@Row fieldAccess 和测试用例 org.sormula.tests.fieldaccess。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-12-07
        • 1970-01-01
        • 2014-07-15
        • 2012-12-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多