A clear example of the difference between DTO, POCO (POJO) and Value Object


Inspired by the article about the differences between DTO, POCO and Value Object on Habrahabr: DTO vs POCO vs Value Object, as well as the question POCO vs DTO.

There are no concrete examples anywhere. Please provide a specific example with a small description (or also an example) of where and how to use it and for what.

UPD

Great answers. Thank you all.

Another small question about using POCO. When and how rational is it cram logic into objects? For example, I have a service layer that returns POCO, which methods can I insert there? Let's say I need to validate the Customizer, OK, I made the Validate method in POCO, so far I don't need to go into the database for validation - everything is fine, but as soon as it is needed, the idea no longer seems so good. Or am I wrong? Now I have an application where almost all actions are performed by the business layer, in models only simple methods of the type GetFullName, and in fact I operate DTO-hami. So, how to keep track of that fine line "what's in the POCO, what's in the service" or in general "all the logic in the services, operate DTO"?

Author: Дух сообщества, 2015-10-07

5 answers

Let's imagine some online store. This store has a web interface and an application server that handles the logic. A certain user wants to make an order. To do this, they need to perform a number of actions: add the desired products to the cart and confirm the order.

In order to do this, the Order class can exist on the application server:

public class Order
{
    private ItemDiscountService _itemDiscountService;
    private UserService _userService;

    public Order(ItemDiscountService itemDiscountService, UserService userService)
    {
        _itemDiscountService = itemDiscountService;
        _userService = userService
    }

    public int Id { get; set; }
    public List<Item> Items { get; set; }
    public decimal Subtotal { get;set; }
    public decimal Discount { get; set; }

    public void AddItem(Item item)
    {
        Items.Add(item);
        CalculateSubtotalAndDiscount();
    }

    public void CalculateSubtotalAndDiscount()
    {
        decimal subtotal = 0;
        decimal discount = 0;
        foreach (var item in Items)
        {
            var currentCost = item.Cost * _itemDiscountService.GetDiscountFactor(item) * _userService.GetCurrentUserDiscountFactor();
            subtotal += currentCost;
            discount += item.Cost - currentCost;
        }

        Subtotal = subtotal;
        Discount = discount;
    }
}

This class contains the data and the logic for changing it. It is not inherited from any a specific class from a third-party library or from a third-party class and is quite simple - Plain Old CLR/Java Object.


When a user adds something to the shopping cart, this information is passed to the application server, which calls the AddItem method in the Order class, which recalculates the cost of the goods and the discount, thereby changing the order status. You need to display this change to the user, and to do this, you need to pass the updated state back to client.

But we can't just pass an instance of our Order or a copy of it, since it depends on other classes (ItemDiscountService, UserService), which in turn may depend on other classes that may need a database connection, etc.

Of course, they can be duplicated on the client, but then all our logic, the DB connection string, and so on will be available on the client, which we absolutely do not want to show. So to just pass the updated state, we can make a special class for this:

public class OrderDto
{
    public int Id { get; set; }
    public decimal Subtotal { get; set; }
    public decimal Discount { get; set; }
    public decimal Total { get; set; }
} 

We can put in it the data that we want to transfer to the client, thereby creating a Data Transfer Object. It can contain absolutely any attributes we need. Including those that are not in the Order class, for example, the Total attribute.


Each order has its own identifier - Id, which we use to distinguish one order from another. While in server memory applications can have an order with Id=1, containing 3 items, the database can store the same order, with the same ID, but containing 5 items. This can occur if we read the order status from the database and changed it in memory without saving the changes to the database.

It turns out that despite the fact that some values of the order in the database and the order in the memory of the application server will be different, it will still be the same object, since their IDs are match.

In turn, the value of the cost - 100, the ID number - 1, the current date, the name of the current user - "Петрович" will be equal to similar values only when these values are completely the same, and nothing else.

I.e. 100 can only be equal to 100, "Петрович" can only be equal to "Петрович" , etc. It doesn't matter where these objects are created. If their values are completely the same , they will be equal. Such objects are called Value Object.

In addition to the existing Value Object of the decimal or string type, you can also create your own. In our example, we could create a type OrderPrice and put the fields thereSubtotal, Total and Discount.

public struct OrderPrice
{
    public decimal Subtotal;
    public decimal Discount;
    public decimal Total;
}

In c# there is a suitable way to create meaningful types that are compared by value and are completely copied when assigned.


UPDATE As for the updated question (though it's really a separate big question, as Discord noted):

When we develop an application, we work with a particular subject area. This domain can be expressed in the form of a model and actions that change the state of this model. All this can be represented as a set of classes. Such classes contain both data (in the form of class fields) so are the actions that manipulate this data (in the form of methods).

In principle, there are no restrictions on placing data or methods by class. You can generally put everything in one class and it will work fine. The main problem is that such code will be more difficult, and therefore more expensive to maintain. Since everything will be intertwined with each other - any changes can introduce a lot of errors, etc. Therefore, to achieve a more "cheap" code, we begin to structure it somehow, break it into modules, etc.

We can decompose data into some classes, and methods into others, and this is it will also work and will be even more modular. But it can still carry a number of disadvantages. Looking at a bunch of data, it may not be obvious what might be happening to them at all, or who might need them. It's the same with a bunch of methods. Therefore, to make it even more convenient, you can decompose the data into classes by somehow grouping them in an understandable way. The same goes for methods. Order data, user data, product data, etc. can become separate classes, as well as classes with corresponding methods. It will be even more modular and intuitive. But any approach has its pros and cons.

For example, in our online store there are various products, the logic of calculating the price of which can be quite complex. Imagine that there is a base class Item, and a set of derived classes:

public class Item
{
  public int Id {get;set;}
  public string Name {get;set;}
  public decimal BaseCost {get;set;}
  public decimal Cost {get;set;}
}

public class Boots : Item { ... }
public class Shirt : Item { ... } 
public class Pants : Item { ... }

Since we have logic in separate classes, let's imagine that there is a class ItemCostService that can calculate the cost of a product. Then, due to the presence of a large number of different conditions, it can look something like this:

public class ItemCostService
{
  public decimal CalculateCost(Item item)
  {
    if(item is Boots)
    {
      item.Cost = ...
    }
    else if (item is Shirt)
    {
      item.Cost = ...
    }
    else if ....  
  }
}

And there can be many places in the program where, depending on the specific type of product, there should be different behavior. Of course, this will all work. But as soon as we have a new product type, or the logic of processing an existing product type changes, we will have to change the code in a large number of places wherever such conditions are present. This is more difficult than changing everything in one place, it takes longer and is fraught with the fact that you can forget something to do.

In this question, we are talking about languages whose main paradigm is OOP. This means that there is a ready-made infrastructure that supports the basic principles of OOP. To follow this paradigm and benefit from the ready-made infrastructure, we can change our classes by adding cost calculation logic to them, changing it as necessary in derived classes.:

public class Item
{
  ...
  public virtual void CalculateCost() { ... }
}

public class Boots : Item
{
  public override void CalculateCost() { ... }
}

Each derived type will be able to determine the logic of its own behavior. All of it will be in one place, next to the data. And which of the specific methods to call will determine the infrastructure itself, saving us from this headache. In this example, this approach will be more convenient, because we will no longer need to create a bunch of if ' s throughout the code, which will only simplify the program and make changes easier.

Well, again-it all depends on the situation. There is no silver bullet and in different cases it is worth using different approaches that will be cheaper in the future. for each specific situation. A little more about OOP and the rest can be found in my article here.

 71
Author: ApInvent, 2017-09-15 22:30:30

I will give my interpretation of what is said in the article. However, I do not agree that DTO and VO do not overlap.

POCO is a class that is not nailed to the architecture of any library. The programmer is free to choose the class hierarchy (or lack thereof). For example, a library for working with a database will not force you to inherit "user" from "entity" or "active record". Ideally, class purity doesn't even need attributes.

This approach frees your hands It allows programmers to build an architecture that is convenient for them, use existing classes to work with other libraries, etc. However, it is not without problems, for example, using POCO may require magic at runtime: generating inherited classes in memory, etc.

An example of a POCO is any class that is not inherited from a library-specific base class, is not cluttered with conventions and attributes, but that nevertheless less than can be fully used by this library.

DTO is a class with data, but no logic. It is used to transfer data between application layers and between applications, for serialization and similar purposes.

An example of a DTO is any class that contains only fields and properties. It should not contain methods for getting and changing data.

VO is a class that is identified by a value. If not resorted to to overload comparison operators and other methods, then in C# the class will not be VO (for classes, by default, equality is a reference to the same object, reference equality), but the structure will be (for structures, by default, equality is the equality of all fields). In this case, the class is not limited to the presence of logic. It is recommended to make such classes immutable, but sometimes life forces you to deviate from this rule.

An example of VO is any class that implements equality through the equality of the data contained in it.


Consider an example:

struct Point {
    public int X;
    public int Y;
}

This type is a POCO, since it is not inherited from non-user types. It is a DTO because it contains only data and can be used to transfer data between processes. And it is VO, because two points with equal coordinates will be equal.

class Point {
    public int X;
    public int Y;
}

If you replace struct with class, the VO status will disappear, since two points with equal coordinates will be unequal. To Once again fully named VO, you will need to implement Equals, operators, and comparison interfaces.

class Point {
    public int X;
    public int Y;

    void Move (int deltaX, int deltaY) { ... }
    void IsWithin (Rect rect) { ... }
}

After adding the methods, the DTO status will disappear, since the class already contains logic for changes and calculations.

class Point : DbEntity {
    [Property]
    public int X;

    [Property]
    public int Y;

    void Move (int deltaX, int deltaY) { ... }
    void IsWithin (Rect rect) { ... }
}

But in this form, it is not even POCO, because the class has acquired a base type and attributes from a third-party library.

 36
Author: Kyubey, 2015-10-07 16:25:34

These are disjoint concepts. In principle, they can not be placed on the same plane (as done in the article on habr).

POCO / POJO is an approach to writing classes-business logic entities. As entities, POCOs contain and data, and logic. The "Plain Old" part just shows that to create entity classes, inheritance from a heavy superclass from the framework is not used (like inheritance from EntityObject in the old Entity That is, the essence of the approach - " I use not the harsh mega-duper-fashionable megaclasses from the framework, but the old-fashioned, stupid old-fashioned ordinary objects."

POCO is only used inside BL.

DTO is a pattern that involves using separate classes for data transmission (no state, no logic, just objects with a set of properties). A DTO cannot be an entity of business logic - and, accordingly, it cannot be a POCO.

DTO is used on the boundary between layers/services. At the same time, it can not be a POCO in any way - it is not a full-fledged entity. In addition, DTO has never had the problem of ubiquitous use of "newfangled superclasses", from which it would be possible to return to Plain Old Objects.

Value Objects is a way to represent logically complete objects that do not have ready-made standard types. For example, dates, time, money. Value Objects are not self-contained entities. These are "building blocks" for building entity classes.

Value Object is used wherever necessary. This is an auxiliary type, like DateTime. And it can't be POCO, by definition-because it doesn't represent an entity, and again, the general use of "new newfangled superclasses" for ValueObjects has never been observed, so they just didn't have to become "good old".


In addition to the question: now you have what is called Anemic Domain Model. Following the link, Fowler describes in sufficient detail what is wrong with this:

This is one of those anti-patterns that's been around for quite a long time, yet seems to be having a particular spurt at the moment.

The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together.

For all the horrors Fowler painted, this is quite normal the approach for small, simple applications is because there is little logic in them, and it can be expressed in the form of a transaction script - what you now call services. I.e., each operation is simply written in the form of a script - load this, that, change that, save.

Anemic has its advantages - because there is no logic in the entities - they can be safely passed outside the Service Layer. Actually, that's why you don't see the difference between POCO and DTO - because you are calmly giving away your POCO-BE That is, you do not have the problem that the DTO pattern is designed to solve, and therefore you can not understand why separate classes-DTO are needed - because in your case they are really not needed.

The difference manifests itself just when moving away from Anemic - in essence, logic is added (your Validate, for example). And the entity given out from BL can suddenly get into the database, for example, at the time of rendering the view. It is this problem that is solved by patterns DTO/LocalDTO - you are just a stripped-down class, without logic, which can be safely passed out.

The use of BE is automatically limited to BL, and the use of the term POCO (as business logic encoded in ordinary objects, and not in the inheritors of classes from the framework) is also limited.

 12
Author: PashaPash, 2020-06-12 12:52:24

I want to try to answer, although I don't know if I know enough about the subject. If anything, please correct it.

Poco. I have an association that Poco is directly mapping to a DB table. Although, judging by the definition, this is not necessarily the case. By definition, just a simple class with simple fields, properties, and methods of type string, int... Can have logic.

public class Person
{
    public string Email { get; set; }
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }

    public override string ToString()
    {
        return String.Format("Name: {0}, E-mail: {1}, Birth Date: {2}", Name, Email, BirthDate);
    }

    //EDIT! some other logic
    public void SetEmailToLowerCase()
    {
        Email = Email.ToLower();
    }
}

ValueObject. A unit from the DDD paradigm that does not need an id, because it is unmutable, that is, it does not change along the way. Apparently, it can have a logic that does not prevent it from being unmutable.

public class PersonValueObject
{
    public string Email { get; private set; }
    public string Name { get; private set; }
    public DateTime BirthDate { get; private set; }

    public PersonValueObject(string email, string name, DateTime birthDate)
    {
        Email = email;
        Name = name;
        BirthDate = birthDate;
    }

    public override string ToString()
    {
        return String.Format("Name: {0}, E-mail: {1}, Birth Date: {2}", Name, Email, BirthDate);
    }

    //EDIT!
    public override bool Equals(object obj)
    {
        var other = obj as PersonValueObject;
        if (other == null) return false;
        else return (
            Email == other.Email
            && Name == other.Name
            && BirthDate == other.BirthDate
            );
    }
}

DTO. For data transfer between different systems or different layers of the same system. Must not have logic. Although, in some cases, why not have logic there. Only not such that the state of the object changes, but such that a value is calculated based on the state. But for clarity, I will write without logic.

public class PersonDto
{
    public string Email { get; set; }
    public string Name { get; set; }
    public DateTime BirthDate { get; set; }
}

To the question of what the difference between Poco and Dto I would answer that Dto is only for data transfer, and Poco is a simple data model.


UPD

Another small question about using POCO. When and how much is it rational to cram logic into objects?

I don't know what's best in your case. But I know that everything falls into place if you use DDD. I will try to say about the concept of an Aggregate. Maybe this will not be useful in this case, because DDD has large entry threshold.

An example of an Aggregate can be here.

  • An aggregate, as far as I understand it, is a logical bundle of entities combined into one whole, which has an ID. The aggregate can not be broken, it always has the correct data.
  • The aggregate is well supported by validation and other logic. However, I have heard that validation refers to the persistence logic, that is, it should be implemented by repositories.
  • The properties of the aggregate are private. Changing data it is carried out through the methods of the Aggregate. The data is processed immediately, the state of the aggregate is changed, or an exception is called.
  • As far as I understand, the Aggregate sends messages (mediator pattern) when changing the state. Other aggregates subscribe to these messages in order to respond correctly.
  • The aggregate is taken from the repository. It is also sent to the repository to save the data. If we take into account that one aggregate can combine data from several database tables, then the implementation of repositories turns out to be quite sophisticated. (I haven't tried it with NHibernate, though)
  • The presentation layer only works with DTO.
  • The data write operation goes something like this: within a single transaction, aggregates are retrieved from the repositories, aggregate methods are called to change their states, and aggregates are stored in the repositories.
  • the transformation of Dto -> aggregate and aggregate -> Dto takes place with the help of DtoAssembler (and in order to the transformation Dto->Aggregate would take into account the sequence of changes in the properties Dto). (Edit:) Some logic can be used in domain services called by this assembler. (I used to say that domain services can be called by the aggregate, but somehow it is not logical to pass services to the aggregate, especially if you can withstand IoC)

This is a theory, but in practice it is more difficult. In addition, this is only my experience, which I do not have so much. There is no such thing as only this way and not otherwise. I understood these points for myself, and took them from a more experienced colleague and from the pluralsight " DDD " lecture course. I accept criticism.

 7
Author: Andrey K., 2015-11-23 11:07:24

About Value Object-I recently saw an example: https://folkprog.net/value-object-y-u-symfony-formakh/ But there in the context of Symfony form. As far as I understand, the convenience lies in storing something like a single value, but which consists of several simple (scalar?) values. Something like a vector. Accordingly, and the comparison logic is the same, by values (in contrast to entity, where by ID)

 1
Author: Jonny, 2016-08-16 10:34:53