简体   繁体   English

Spring MVC 3 - 将“不可变”对象绑定到表单

[英]Spring MVC 3 - Binding an 'immutable' object to a form

I have several thoroughly unit-tested and finely crafted rich DDD model classes, with final immutable invariants and integrity checks. 我有几个完全经过单元测试和精心制作的富DDD模型类,具有最终的不可变不变量和完整性检查。 Object's instantiation happens through adequate constructors, static factory methods and even via Builders. 对象的实例化通过适当的构造函数,静态工厂方法甚至通过构建器来实现。

Now, I have to provide a Spring MVC form to create new instances of some classes. 现在,我必须提供一个Spring MVC表单来创建一些类的新实例。

It seems to me (I'm not an expert) that I have to provide empty constructor and attribute's setters for all form's backing classes I want to bind. 在我看来(我不是专家)我必须为我要绑定的所有表单的支持类提供空构造函数和属性的setter。

So, what should I do ? 所以我该怎么做 ?

Create anemic objects dedicated to form backing and transfer the informations to my domain model (so much for the DRY principle...) calling the appropriate methods / builder ? 创建专门用于形成支持的贫血对象,并将信息传递给我的域模型(对DRY原则来说太多了......)调用适当的方法/构建器?

Or is there a mecanisms that I missed that can save my day ? 或者是否有一些我错过的机会可以挽救我的一天? :) :)

Thank you in advance for your wisdom ! 提前感谢您的智慧!

The objects that are used for binding with the presentation layers are normally called view models and they are DTOs purposed toward displaying data mapped from domain objects and then mapping user input back to domain objects. 用于与表示层绑定的对象通常称为视图模型,它们是DTO,用于显示从域对象映射的数据,然后将用户输入映射回域对象。 View models typically look very similar to the domain objects they represent however there are some important differences: 视图模型通常看起来与它们所代表的域对象非常相似,但是存在一些重要的差异:

  1. Data from the domain objects may be flattened or otherwise transformed to fit the requirements of a given view. 来自域对象的数据可以被展平或以其他方式变换以适合给定视图的要求。 Having the mapping be in plain objects is easier to manage than mappings in the presentation framework, such as MVC. 使映射在普通对象中比在演示框架中的映射(例如MVC)更容易管理。 It is easier to debug and detect errors. 调试和检测错误更容易。

  2. A given view may require data from multiple domain objects - there may not be a single domain object that fits requirements of a view. 给定视图可能需要来自多个域对象的数据 - 可能没有单个域对象适合视图的要求。 A view model can be populated by multiple domain objects. 视图模型可以由多个域对象填充。

  3. A view model is normally designed with a specific presentation framework in mind and as such may utilize framework specific attributes for binding and client side validation. 视图模型通常在设计时考虑了特定的表示框架,因此可以利用框架特定的属性进行绑定和客户端验证。 As you stated, a typical requirement is for a parameterless constructor, which is fine for a view model. 正如您所说,典型的要求是无参数构造函数,这对于视图模型来说很好。 Again, it is much easier to test and manage a view model than some sort of complex mapping mechanism. 同样,测试和管理视图模型要比某种复杂的映射机制容易得多。

View models appear to violate the DRY principle, however after a closer look the responsibility of the view model is different, so with the single responsibility principle in mind it is appropriate to have two classes. 视图模型似乎违反了DRY原则,但仔细观察后,视图模型的责任是不同的,因此在考虑单一责任原则的情况下,有两个类是合适的。 Also, take a look at this article discussing the fallacy of reuse often lead by the DRY principle. 另外,请看一下这篇文章,讨论通常由DRY原则引导的重用谬误。

Furthermore, view models are indeed anemic, though they may have a constructor accepting a domain object as a parameter and a method for creating and updating a domain object using the values in the view model as input. 此外,视图模型确实是贫血的,尽管它们可能具有接受域对象作为参数的构造函数以及使用视图模型中的值作为输入来创建和更新域对象的方法。 From experience I find that it is a good practice to create a view model class for every domain entity that is going to be rendered by the presentation layer. 根据经验,我发现为每个将由表示层呈现的域实体创建视图模型类是一种很好的做法。 It is easier to manage the double class hierarchy of domain objects and view models than it is to manage complex mapping mechanisms. 管理域对象和视图模型的双层层次结构比管理复杂的映射机制更容易。

Note also, there are libraries that attempt to simplify the mapping between view models and domain objects, for example AutoMapper for the .NET Framework. 另请注意,有些库试图简化视图模型和域对象之间的映射,例如.NET Framework的AutoMapper

I solved this by creating a DTO Interface: 我通过创建DTO接口解决了这个问题:

public interface DTO<T> {
    T getDomainObject();

    void loadFromDomainObject(T domainObject);
}

public class PersonDTO implements DTO<Person> {
    private String firstName;
    private String lastName;

    public PersonDTO() {
        super();
    }

    // setters, getters ...

    @Override
    public Person getDomainObject() {
        return new Person(firstName, lastName);
    }

    @Override
    public void loadFromDomainObject(Person person) {
        this.firstName = person.getFirstName();
        this.lastName = person.getLastName();
    }

    // validation methods, view formatting methods, etc
}

This also stops view validation and formatting stuff from leaking into the domain model. 这也会阻止视图验证和格式化内容泄漏到域模型中。 I really dislike having Spring specific (or other framework specific) annotations (@Value, etc) and javax.validation annotations in my domain objects. 我真的不喜欢在我的域对象中使用Spring特定(或其他特定于框架)的注释(@Value等)和javax.validation注释。

Yes you will need to create Objects for the form to take all the input, and the update the your model with this objects in one operation. 是的,您需要为表单创建对象以获取所有输入,并在一次操作中使用此对象更新模型。

But I wont call this objects anemic (especially if you do DDD). 但是我不会把这个对象称为贫血(特别是如果你做DDD)。 This objects represent one unit of work. 这个对象代表一个工作单元。 So this are Domain Concepts too! 所以这也是Domain Concepts!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM