Java 如何在持久层和域对象之间正确映射
假设我有一个表示个人的域java类:Java 如何在持久层和域对象之间正确映射,java,domain-driven-design,Java,Domain Driven Design,假设我有一个表示个人的域java类: class Person { private final String id; // government id private String name; private String status; private Person(String id, String name) { this.id = id; this.name = name; this.status = "
class Person {
private final String id; // government id
private String name;
private String status;
private Person(String id, String name) {
this.id = id;
this.name = name;
this.status = "NEW";
}
Person static createNew(String id, String name) {
return new Person(id, name);
}
void validate() {
//logic
this.status = "VALID";
}
public static final class Builder {
private String id;
private String name;
private String status;
private Builder() {
}
public static Builder aPerson() {
return new Builder();
}
public Builder id(String id) {
this.id = id;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder status(String status) {
this.status = status;
return this;
}
public Person build() {
Person person = new Person(id, name);
person.status = this.status;
return person;
}
}
我将这个域类对象存储在一个数据库中,它是一个具有相同字段+getter和setter的常规类。当前,当我想存储对象时,我创建了新的PersonDocument
(数据存储在mongo中),使用getter和setter并保存它。当我想从DB获取它时,它变得复杂了。我希望我的域对象只公开必要的内容,因为当前的业务逻辑只是创建和验证。简单地说:
Person p = Person.createNew("1234", "John");
p.validate();
repository.save(p);
另一方面,它变得复杂,目前有一个生成器,允许在任何状态下创建对象。我们确实相信数据库中存储的数据有一个适当的状态,因此可以通过这种方式创建数据,但缺点是有一个可用的公共API,允许任何人做任何事情
最初的想法是使用MapStruct java映射库,但它确实使用setter来创建对象,应该避免在域类中公开setter(据我所知)
有什么建议如何正确地做吗 您的问题可能来自两个相互冲突的需求:
documenttodocument()
引入到生成与Mongo兼容的json文档的对象中,还可以将Person fromDocument(Document)
引入反序列化
大多数人不喜欢这种解决方案,因为它将技术与对象“耦合”。这是好事还是坏事?取决于您的用例。您希望优化哪一个:更改业务逻辑还是更改技术?如果您不打算经常更改技术,也不打算在完全不同的应用程序中使用相同的类,那么就没有理由将技术分开。我这样做:
作为域实体的人员具有状态(定义实体的实体字段,而不是您的“状态”字段)和行为(方法)
数据库中存储的只是状态。然后我在域中创建一个“PersonStatus”接口(使用我们需要持久化的字段的getter方法),以便PersonRepository处理状态
Person实体实现PersonStatus(或者,您可以放置一个返回状态的静态方法来代替它)
在基础结构中,我也有一个PersonDB类实现PersonStatus,这是持久性模型
因此:
域模型:
// ENTITY
public class Person implements PersonStatus {
// Fields that define status
private String id;
private String name;
...
// Constructors and behaviour
...
...
// Methods implementing PersonStatus
@Override
public String id() {
return this.id;
}
@Override
public String name() {
return this.name;
}
...
}
// STATUS OF ENTITY
public interface PersonStatus {
public String id();
public String name();
...
}
// REPOSITORY
public interface PersonRepository {
public void add ( PersonStatus personStatus );
public PersonStatus personOfId ( String anId );
}
基础设施:
public class PersonDB implements PersonStatus {
private String id;
private String name;
...
public PersonDB ( String anId, String aName, ... ) {
this.id = anId;
this.name = aName;
...
}
@Override
public String id() {
return this.id;
}
@Override
public String name() {
return this.name;
}
...
}
// AN INMEMORY REPOSITORY IMPLEMENTATION
public class InmemoryPersonRepository implements PersonRepository {
private Map<String,PersonDB> inmemorydb;
public InmemoryPersonRepository() {
this.inmemoryDb = new HashMap<String,PersonDB>();
}
@Override
public void add ( PersonStatus personStatus );
PersonDB personDB = new PersonDB ( personStatus.id(), personStatus.name(), ... );
this.inmemoryDb.put ( personDB.id(), personDB );
}
@Override
public PersonStatus personOfId ( String anId ) {
return this.inmemoryDb.personOfId ( anId );
}
}
Robert Bräutigam的句子很好: 两个相互冲突的要求 但艾伦·凯的另一句话更好: 很抱歉,我很久以前就为这个话题创造了“对象”这个词 因为它让很多人把注意力集中在较小的想法上,而大的想法上 “这就是信息。”~Alan Kay 因此,与其处理冲突,不如改变方法来避免冲突。我发现的最好的方法是采用函数方法,通过将域更改表示为事件来避免类中不必要的状态和突变 为了将类(聚合、V.o.和/或实体)映射到持久性,我执行以下操作:
persistence.apply(Event)
),持久层都有一个apply方法。这样,你的坚持就知道发生了什么,也知道发生了什么;只要事件具有保持更改所需的所有数据;可以将更改应用到(如果需要,甚至可以使用行为!)查看(值得在本博客中查看所有DDD系列)以查看类似的实现。基本上可以归结为两件事,这取决于您愿意在必要的基础设施上添加多少额外工作以及对ORM/持久性的约束程度 使用CQRS+ES模式 在更大、更复杂的领域中使用的最明显的选择是使用CQRS(命令/查询责任分离)“事件源”模式。这意味着,每个可变操作都会生成一个持久化的事件 加载聚合后,所有事件都将从数据库中加载,并按时间顺序应用。一旦应用,聚合将具有其当前状态 CQR只是意味着,您可以将读操作和写操作分开。写入操作将通过创建事件(通过应用命令)在聚合中发生,这些事件通过事件源存储/读取 其中“查询”是对投影数据的查询,它使用事件创建对象的当前状态,用于查询和rea
...
Person person = new Person ( "1", "John Doe", ... );
personRepository.add ( person );
...
PersonStatus personStatus = personRepository.personOfId ( "1" );
Person person = new Person ( personStatus.id(), personStatus.name(), ... );
...