3

LocalDTO term refers to Fowler's article.

Say I have a domain entity Person:

public class Person {
    private String id;
    private String name;
    private int age;
}

This entity has its own semantics in the domain & application layer and has its behavior. In other words, it is not an anemic model.

Now I have a presentation class (assume an MVController or an MVPresenter) that needs to show a list of Persons and have a form that user fills to update a stored Person.

That's why I currently have the following contract.

public interface PersonService {
    List<PersonDto> getPersons();

    void updatePerson(PersonDto personDto);
}

And now my presentation class depends on person service. It does not know that Person class exists and does not have not access to the domain world. I am aware that this is good thing.

However, the application is a desktop application. Fowler in his article says that DTO's should be used for remote communication (I understand why). In a desktop application, there is no remote communication (actually there is - I use DTOs there anyway). So my question, is this PersonDto a LocalDTO?

If I follow Fowler's advice:

start with a locally invocable Service Layer whose method signatures deal in domain objects

I end up with:

public interface PersonService {
    List<Person> getPersons();

    void updatePerson(Person p);
}

I have the following problem: The presentation class will be able to call (say for example) person.setAge(int x). Which something I want to avoid since I consider this a "leakage". Also, to the application, calling setAge does only the validation. It does not have a meaning if this person is not persisted (passed to the service).

In order to avoid this leakage and make the domain entity read-only, I can have the setter package private. Then service can manipulate Person objects and presentation class can only read from them. Which is great.

However, there is a problem with this approach as well. Testing. Testing the presentation classes require to create models. For example, given a person, is its name and age on the form? If Person objects are read only in presentation layer, I cant create Person objects for (in) presentation layer tests. But if I had DTOs, who cares? Just new PersonDto.

So what is the solution? DTO's?

3
  • 1
    Without more information about the behavior and semantics of Person, it's hard to offer suggestions. Your mention of setAge is suspicious as that's not behavior, so it's unclear what behavior the UI has here. From a layering standpoint, there's no problem having the presentation layer be aware of domain behavior -- in fact it has to be aware of it. The presentation layer would have a JavaBean, which is a form of DTO. And I'm unclear on what testing you're trying to do in the presentation layer. Nov 25, 2021 at 17:45
  • Testing the presentation layer is widget state. When a Person is above 18 years old, the isAdultCheckBox is selected (ticked) and cannot be changed. Something like this. When the X, the Button Y cannot be pressed. Regarding my actual problem, I cannot recall what exactly I was thinking back then but I have solved it on my own. Thanks for taking the time to read and comment my question. :)
    – George Z.
    Nov 25, 2021 at 17:55
  • @TedM.Young Sorry, I forgot the tag. :)
    – George Z.
    Nov 25, 2021 at 19:59

4 Answers 4

1

It should be possible to test the presentation classes from the described scenario using property shadowing by having test dedicated classes with getters and setters extending the classes with the private package setters and pass around the test dedicated instance.

A possible solution for avoiding

The presentation class will be able to call (say for example) person.setAge(int x).

could be builder design pattern for classes encapsulating only fields with getters instead of properties. Since the problem to avoid is calling setters on existing instances, that is calling setters on instances created in other layers, the presentation layer could create read only instances. Having immutable like classes could be a solution for passing information between all the application layers including the presentation and service ones.

-2

OK so how i understand your problem:

You have a UI where you can create and edit a "person" and a domain object for Person which implements various constraints on its properties.

You dont want the UI to set properties on the domain object, because that would generate errors.

You do want to pass all the "person" properties back to the Application layer to then be applied to a Person object, returning any validation errors to the UI

The solution you propose is to have an anemic PersonDto object with the properties and no logic used by the UI to pass the person data around.

This sounds very much like a ViewModel to me and is fine. But not a great solution.

The problem is that unless you use your domain objects in your view models you will end up with multiple versions of PersonForViewX all implementing similar logic

The best solution is to switch to an anemic Person object and move your business logic to service classes.

You can now reuse the Person object through out your code base with impunity and separate the domain logic eg. BuyAlcholService.ValidateCustomer(Person p) for use where required

4
  • So either LocalDTO "anti pattern", either anemic model "anti pattern". Right?
    – George Z.
    Jun 13, 2021 at 23:22
  • ADM is only an antipattern in monolith desktop applications where you pass objects by reference and hold them for multiple operations. When you pass as data over the wire your dtos are the DMs
    – Ewan
    Jun 15, 2021 at 19:49
  • Sorry, but I did not understand your comment. "ADM(anemic domain model, I guess) is an antipattern in monolith dekstop applications". Isn't this what I have?
    – George Z.
    Jun 15, 2021 at 20:39
  • 1
    your talk of services and MVC lead me to assume not. But if you are then you can follow OOP and call person.,setAge() in your view. I find the MF writes from the monolith all in memory perspective, person.setage() encapsulates all the logic about setting the age and its fine to call it whenever. DDD comes from a distributed application approach with services and models and the ADO approach works better. It sounds like you are caught half way between both and as a result getting muddled advice from these types of article.
    – Ewan
    Jun 16, 2021 at 13:44
-2

Consider changing the interface of your person service to include:

public interface PersonService {
    List<ReadablePerson> getPersons();
}

ReadablePerson would be an interface implemented by your Person entity, which includes functions such as getAge but does not include setAge. That would stop your presentation layer changing the age, as long as it doesn't type cast the object back to Person.

1
  • I would love to know why this answer was downvoted. To me, this is a totally appropriate solution for the given problem. What am I missing? Dec 21, 2021 at 6:53
-3

I have the following problem: The presentation class will be able to call (say for example) person.setAge(int x). Which something I want to avoid since I consider this a "leakage".

One option is to just avoid writing person.setAge(int x) or similar in your presentation layer.

3
  • why posting second answer instead of editing and expanding this one
    – gnat
    Jun 12, 2021 at 13:51
  • 3
    I do not like this answer (-1). I don't think good software is built by 'just do x'. That's why my question exists. Encapsulation and boundaries exists (at least) in OOP for years. And there is a reason for that. "Just do not call set" to my eyes is the same with "just make everything public".
    – George Z.
    Jun 12, 2021 at 15:28
  • I posted two answers because I wanted to suggest two different things, I thought people might want to vote on them separately. At present both are downvoted but by different amounts.
    – bdsl
    Jun 12, 2021 at 17:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.