2
  1. What is the right way to create DTOs from business objects?
  2. Who should be responsible for creating them? BO/DTO itself from BO/some static factory?
  3. Where should they reside in code if I have, f.e. some core library and a specific service API library that I need DTO for? In core library next to BO(which seems incorrect)/in specific library?
  4. If I have encapsulated fields in my BO how do DTO grab them? (obviously in case when BO is not responsible for creating DTOs)

As an example assume that I have some Person BO like this:

class Person
{
    private int age;
    public bool isBigEnough => age > 10;
}

I want age to be an internal state of Person but still I need to communicate my BO to some api. Or having private field in my class that I want to send somewhere already means that it should be public?

  1. Are there any general considerations of how to use DTOs alongside business classes with encapsulated data?

___ Update:

In addition to approaches that @Alexey Groshev mentioned I came accross another one: we separate data of our BO class into some Data class with public accessors. BO wraps this data with its api(probably using composition) and when needed it can return its state as Data class as clone. So dto converter will be able to access Domain object's state but won't be able to modify it(since it will be just a copy).

steavy
  • 1,483
  • 6
  • 19
  • 42

2 Answers2

1

There're multiple options available, but it would be difficult to recommend anything, because I don't know the details about your project/product. Anyway I'll name a few.

  1. You can use AutoMapper to map BOs to DTOs and vise versa. I personally dislike this approach, because it's quite difficult (but possible) to keep it under control in medium/large sized projects. People don't usually bother to configure mappings properly and just expose internal state of their objects. For example, your isBigEnough would disappear and age would become public. Another potential risk is that people can map DTOs to/from EF/Hibernate objects. You can find some articles which explain why it's considered to be a bad practice.

  2. As you suggested, a BO can create DTO by itself, but how would you implement this approach? You can add methods or factory methods to your entities, e.g. public PersonDto ToDto(). Or you can add an interface, e.g. public interface IDtoConvertable<T> { T ToDto(); }, and choose which entity or aggregate root will implement it. Your Person class would look like this class Person : IDtoConvertable<PersonDto> {... public PersonDto ToDto() {...} }. In both cases DTO namespace/assembly must to accessible by entities which sometimes can be a problem, but usually it's not a biggie. (Make sure that DTOs cannot access entities which is much worse.)

  3. (C#) Another option is to return a delegate which creates DTO. I decided to separate it from (2), because entity doesn't really create DTO by itself, but rather exposes a functionality which creates DTO. So, you could have something like this public Func<PersonDto> ToDto() {...}. You might want to have an interface as in (2), but you get the idea, don't you? Do I like this approach? No, because it makes code unreadable.

As you see, there are more questions than answers. I'd recommend you to make a few experiments and check what works for you (your project) and what doesn't.

Alex G.
  • 909
  • 1
  • 9
  • 16
  • So statement 1 and 3 will violate and throw away all the oop. Probably point 2 is the best one, though it requires your BO to know about absolutely all dtos in your project/solution. And this will become a big burden as your api forest grows. So basically as conclusion, as I understand, we have that dto concept is oop-unfriendly. – steavy Jun 07 '16 at 09:31
  • I don't think (1) *always* violates OOP. If I were you, I'd still choose it because BOs should not know anything about DTOs - they belong to infrastructure/application layer. You can convert necessary fields to read-only properties, e.g. `public int Age { get; private set;}` so AutoMapper could access them. It's the price you have to pay. You might also want to look at http://stackoverflow.com/questions/678217/best-practices-for-mapping-dto-to-domain-object – Alex G. Jun 07 '16 at 12:16
0

I think the answer to question 5 will address the other questions too.

Are there any general considerations of how to use DTOs alongside business classes with encapsulated data?

Remember, a DTO is solely to transfer data. Do not concern yourself with implementing any kind of rules in the DTO. All it is used for is to move data from one subsystem to another (NOT between classes of the same subsystem). How that data is used in the destination system is out of your control -- although as the God programmer you inherently know how it is going to be used, DO NOT let that knowledge influence your design -- and therefore there should be no assumptions expressed as behaviour or knowledge accessors -- so, no isBigEnough.

  • My `Person` class is part of my own Domain model. Yes, absolutely no business logic in DTOs. Assume I need to send it to some external service as `PersonDto`. How do I create one? Many of my data fields are encapculated in `Person` and I need to retrieve them from there. And, as you suggested not to let it affect my design, I don't want to make my fields public. – steavy Jun 06 '16 at 19:44
  • The subsystem owning the `Person` class should be the only one that knows *how* to create `PersonDto`. Have a factory in that subsystem. Note: It is OK if the factory is accessible from outside. Whilst clients will then be able to create `PersonDto` objects, *how* they are actually created will remain hidden, thus preserving encapsulation. –  Jun 07 '16 at 12:32
  • Yes, that's what I thought about in first place. But is it correct? Should it be part of Domain? I would rather say no, than yes. It's just some infrastructure stuff than Domain. Also it's much more clean to keep such things in place where they are really used. And as a final con. of this approach I a situation that when you need several dto formats of the same BO. Domain code may become quite messy, I suppose. – steavy Jun 08 '16 at 10:17
  • Well, the biggest DTO you could possibly create of the `Person` class is a structure with all the attributes. What other formats would you need? Remember, the DTO is for transferring data. Nothing else. Not even formatting. –  Jun 08 '16 at 19:30