Reputation: 3457
I'm planning to refactor a legacy .NET project that I'm maintaining, there are mainly 4 projects involved:
My.Company.Web
- The presentation layer. ASP.NET MVC.My.Company.Service
- The service layer, defines service interfaces and contains command handlers. Invoked by the web layer.My.Company.DTO
- The assembly that contains DTO classes for data transfer between the domain and web layers.My.Company.Domain
- The domain layer.My.Company.Data
- The persistence layer.When user submits a form to update a person's details, the process looks somewhat like this:
PersonDetailsDTO
which consists of multiple simple fields to hold FirstName
, LastName
, Age
and etc, and a complex type object called AddressDTO
that holds address details: var dto = new PersonDetails(...);
Controller calls the service layer with the DTO:
_personService.UpdateDetails(dto);
In the service:
public void UpdatePerson(PersonDetailsDTO dto)
{
var person = _personRepository.GetById(dto.PersonId);
person.Update(dto);
_personRepository.Update(person);
}
In the Person domain:
public class Person : AggregateRoot
{
private string FirstName;
.... // other fields
private Address Address;
public void Update(PersonDetailsDTO dto)
{
FirstName = dto.FirstName;
.... // other fields
Address.Update(dto.AddressDTO); // update the value object - address
}
}
As you can see here, the Person
domain references the PersonDetailsDTO
. This feels wrong to me as DTOs are means to present the data in the domain to its clients while not exposing the domain itself. and the domain should not be concerned with how it's presented to the clients.
Also IMO the DTOs should live in the My.Company.Service
project as it's the service that defines the operation/data transfer contracts.
But then how would the service pass the details on to the domain? Would it have to be a loooong list of parameters: person.Update(firstName, lastName, age, gender, street1, street2, state, postcode...)
. This certainly looks wrong to me.
Or does the domain have to define yet another set of it's own "DTOs" to receive client data?
I have gone through a LOT of tutorials and SO posts and no one seem to share my concern, so I'm starting to think I'm missing something here, could someone point me to the right direction?
Upvotes: 0
Views: 540
Reputation: 430
You need to implement Object to Object mapping. It means that Service layer is responsible for creating and populating domain objects based on DTO by mapping values from the DTO instance received from client.
You can take advantage of Automapper library to simplify object-to-object mapping. Of course it can be served for domain to DTO if needed.
Upvotes: 1
Reputation: 720
You could do as follows.
You already have a Value Object named Address
similarly you could have another Value Object as PersonalDetails
which will contain firstName, lastName, age, gender etc. and the Person
entity will have two behaviors like updatePersonalDetails()
and updateAddress()
. Your application service will look like below.
public void UpdatePerson(PersonDetailsDTO dto)
{
var person = _personRepository.GetById(dto.PersonId);
Address address = new Address(dto.getStreet1, dto.getStreet2,...);
PersonalDetails details = new PersonalDetails(dto.getFirstName,dto.getLastName(),...);
person.updatePersonalDetails(details);
person.updateAddress(address);
_personRepository.save(person);
}
Upvotes: 1
Reputation: 17683
According to the Dependency inversion principle and DDD, the Value objects
(the DTOs
as you call them: PersonDetailsDTO
and Address
) should be owned by the Domain. This means that they should reside in the Domain namespace/package/project. And BTW, those Value objects
can be used all the way up to the Presentation layer and back.
Secondly, they are not DTOs
(Data transfer objects) because there is no expensive remote call involved, but Value objects
(cohesive data and possible behavior); read more here how to not use them as an anti-pattern.
Last but not least, you should name your methods with terms from the Ubiquitous language (person.Update
does not seem to be from the UL). I'm not saying 100% that is your case because I don't know your domain, I'm just saying that you should pay attention to these important details (naming things).
Upvotes: 2