TL;DR:
A DTO describes the pattern of state transfer. A POCO doesn't describe much of anything except that there is nothing special about it. It's another way of saying "object" in OOP. It comes from POJO (Java), coined by Martin Fowler who literally just describes it as a fancier name for 'object' because 'object' isn't very sexy and people were avoiding it as such.
Expanding...
Okay to explain this in a far more high-brow way that I ever thought would be needed, beginning with your original question about DTOs:
A DTO is an object pattern used to transfer state between layers of concern. They can have behavior (i.e. can technically be a poco) so long as that behavior doesn't mutate the state. For example, it may have a method that serializes itself. For it to be a proper DTO, it needs to be a simple property bag; it needs to be clear that this object is not a strong model, it has no implied semantic meaning, and it doesn't enforce any form of business rule or invariant. It literally only exists to move data around.
A POCO is a plain object, but what is meant by 'plain' is that it is not special and does not have any specific requirements or conventions. It just means it's a CLR object with no implied pattern to it. A generic term. I've also heard it extended to describe the fact that it also isn't made to work with some other framework. So if your POCO has a bunch of EF decorations all over it's properties, for example, then it I'd argue that it isn't a simple POCO and that it's more in the realm of DAO, which I would describe as a combination of DTO and additional database concerns (e.g. mapping, etc.). POCOs are free and unencumbered like the objects you learn to create in school
Here some examples of different kinds of object patterns to compare:
- View Model: used to model data for a view. Usually has data annotations to assist binding and validation for particular view (i.e. generally NOT a shared object), or in this day and age, a particular view component (e.g. React). In MVVM, it also acts as a controller. It's more than a DTO; it's not transferring state, it's presenting it or more specifically, forming that state in a way that is useful to a UI.
- Value Object: used to represent values, should be immutable
- Aggregate Root: used to manage state and invariants. should not allow references to internal entities other than by ID
- Handlers: used to respond to an event/message.
- Attributes: used as decorations to deal with cross-cutting concerns. May only be allowed to be used on certain objects levels (e.g. property but not class, method but not property, etc.)
- Service: used to perform complex tasks. Typically some form of facade.
- Controller: used to control flow of requests and responses. Typically restricted to a particular protocol or acts as some sort of mediator; it has a particular responsibility.
- Factory: used to configure and/or assemble complex objects for use when a constructor isn't good enough. Also used to make decisions on which objects need to be created at runtime.
- Repository/DAO: used to access data. Typically exposes CRUD operations or is an object that represents the database schema; may be marked up with implementation specific attributes. In fact, one of these schema DAO objects is actually another kind of DTO...
- API Contracts: Likely to be marked up with serialization attributes. Typically needs to have public getters and setters and should be lightweight (not an overly complex graph); methods unrelated to serialization are not typical and discouraged.
These can be seen as just objects, but notice that most of them are generally tied to a pattern or have implied restrictions. So you could call them "objects" or you could be more specific about its intent and call it by what it is. This is also why we have design patterns; to describe complex concepts in a few words. DTO is a pattern. Aggregate root is a pattern, View Model is a pattern (e.g. MVC & MVVM).
A POCO doesn't describe a pattern. It is just a different way of referring to classes/objects in OOP which could be anything. Think of it as an abstract concept; they can be referring to anything. IMO, there's a one-way relationship though because once an object reaches the point where it can only serve one purpose cleanly, it is no longer a POCO. For example, once you mark up your class with decorations to make it work with some framework (i.e. 'instrumenting' it), it is no longer a POCO. Therefore I think there are some logical relationships like:
- A DTO is a POCO (until it is instrumented)
- A POCO might not be a DTO
- A View Model is a POCO (until it is instrumented)
- A POCO might not be View Model
The point in making a distinction between the two is about keeping patterns clear and consistent in effort to not cross concerns and lead to tight coupling. For example if you have a business object that has methods to mutate state, but is also decorated to hell with EF decorations for saving to SQL Server AND JsonProperty so that it can be sent back over an API endpoint. That object would be intolerant to change, and would likely be littered with variants of properties (e.g. UserId, UserPk, UserKey, UserGuid, where some of them are marked up to not be saved to the DB and others marked up to not be serialized to JSON at the API endpoint).
So if you were to tell me something was a DTO, then I'd probably make sure it was never used for anything other than moving state around. If you told me something was a view model, then I'd probably make sure it wasn't getting saved to a database, and I'd know that it's ok to put 'hacky' things in there to make sure the data is usable by a UI. If you told me something was a Domain Model, then I'd probably make sure it had no dependencies on anything outside of the domain and certainly no dependencies on any technical implementation details (databases, services etc.), only abstractions. But if you told me something was a POCO, you wouldn't really be telling me much at all other than it is not and should not be instrumented.
Example
This is a weak but accurate example that should be easy to follow.
This could be a POCO, it could also be a DTO. It's just an object; nothing special about it. Looks like a weakly typed property bag, but there's nothing notable about it.
public class CreateUserRequest
{
public string Name { get; set; }
public string Email { get; set; }
}
This is no longer a POCO
public class CreateUserRequest
{
[JsonPropertyName(Name = "name")]
public string Name { get; set; }
[JsonPropertyName(Name = "email")]
public string Email { get; set; }
}
Why isn't a POCO anymore? Because it is clearly a data contract that is instrumented to work with the System.Text.Json serializer. It's closer to a DTO now, but instrumented for a specific framework.
This next one is what happens when you don't make these distinctions
[Table("Users")]
public class CreateUserRequest
{
[Key]
[JsonIgnore]
public string Id { get; set; }
[JsonPropertyName(Name = "name")]
public string Name { get; set; }
[JsonPropertyName(Name = "email")]
public string Email { get; set; }
public int LoginCount { get; set; }
public void IncrementLogin() => LoginCount++;
}
So now this is definitely not a POCO anymore. It looks to be a DTO of some sort, but it's purpose is overloaded. Is it an API contract or a DAO? It appears to be made to work as both a JSON contract AND with a database ORM. Additional instrumentation is required just to prevent it from leaking database PKs out of the REST API. It also has a method that mutates state as if someone is using this as a domain entity. It's not even clear if the developer intended the LoginCount
to be part of the JSON contract or the database schema.
This class is something I see a lot from developers that think they're saving time by reusing classes. They think this is DRY. I suppose you could argue that it's DRY, but in reality, it's contrived, tightly coupled, probably violates 5 other design philosophies, and is just eventually going to screw you in the future.
History
Paraphrased from Fowler's explanation: In a world where objects were fancy (e.g. followed a particular pattern, had instrumentation etc.), it somehow encouraged people to avoid using not-fancy objects to capture business logic. So they gave it a fancy name POJO. If you want an example, the one he refers to is an "Entity Bean" which is one of those kinds of objects that have very specific conventions and requirements, etc.. If you don't know what that is --> Java Beans.
In contrast, a POJO/POCO is just the regular ole object that you'd learn how to create in school.