Behavior in this context is refering to semantic behavior. For example, a Property on a class (i.e. attribute on a domain object) that is used to uniquely identify it has a behavior. While this is not represented in code directly. The expected behavior is that there will not be any duplicate values for that property. Something such as an Address which may have its own identity, but does not exist outside of the context of a Person Entity should still be moved out into it's own object. Thus promoting the Entity into an Aggregate Root.
Furthmore, a property such as Age has no context outside of a Person Entity, and as such, makes no sense to move into a different object. The context would be lost, and therefore you can safely determine that it is a value that is essential to the Person Entity. You could not locate the value otherwise. However that information could easily be stored in a separate location that the unique identifier, hence the confusing reference to behavior. Age could be a lazy loaded value.
So to answer your question. No, it does not violate the Single Responsibility Principle. It is mearly stating that a Person should have only person stuff, and not something like Address, which is more complex and related to a person, should exist as its own entity.
You could easily have operations specific to Address such as verification that it is a valid address. You may not know that at design time, but this whole concept is to break down objects into their smallest parts so that something like this is relatively simple when done after the fact.
Update:
1) In most cases this identity validation is done upon saving an object out to a data store. Which means that the code representing the entity validation exists, but it exists elsewhere. It usually exists with the code that is responsible for issuing the identity value. Which is why I state that the uniqueness is not represented directly in the code for the entity.
2) The correct statement would be that Age
is an attribute that has behavior. You would need to document the fact that Age is lazy loaded so that a developer consuming that property can make an accurate decision on how to consume that property
3) DateOfBirth
usually is a different object; A date object that already has predefined operations on it. In some languages the date object already has a whole domain model defined on it. For example in c# you can specifiy the timezone, if the date is UTC, add and subtract dates to get a timespan. So your assumption about moving DateOfBirth
would be correct.
4) If the only thing that MyEntity
does is delegation and cooridination then no it does not violate SRP. Its single responsibility is to delegate and coordinate and is refered to as the Facade pattern
Frankly, glue classes that merely pass through fields from the data model don't really add significant value to your application, nor are they particularly interesting from a functional perspective.
So before we get too deeply into your individual questions, let me propose a simple foundational architecture:
DB <---> ORM <---> SL/BLL <---> VM <---> V
The DB is your database. The ORM is your Object-Relational Mapper; it communicates with the database via SQL, and exposes CRUD methods. The SL/BLL is your Service Layer/Business Logic Layer; it converts business operations such as CreateInvoice
into CRUD methods for consumption by the ORM. The VM is the ViewModel; it coordinates UI interaction with the SL/BLL. The V is the View; it contains surface-level UI interaction and validation code-behind.
Your sample code is suggestive of C#. In C#, your ORM (very likely Entity Framework) can produce code-generated entity classes that can be glued to domain logic using the partial
keyword, allowing you to write custom domain and validation logic for each entity class without the pain of creating all that boilerplate for DTO's.
A lean architecture like this one should allow you to develop an easily maintainable application with the minimum necessary boilerplate code. Unless you're building an elaborate, crystal-cathedral architecture in Java for a large development team, this representative architecture should be all of the abstraction you will ever need.
Best Answer
To expand on the answer by ques, a value object does not have a surrogate identity such as a customer number, but the object itself is its own natural identity.
Take an address for example. All addresses are unique, and the various components of an address (number, street name etc) make up the addresses identity. When you move house, you don't pick up your address, change the number on the door and take it with you, you get a new address. As such value objects don't change - they are immutable. However you could have 2 or more customers living at the same address, in which case the address value is reusable - it is not tied to a single customer entity.