如何将DTO转换为域对象
我试图将无处不在的语言应用于我的域对象。如何将DTO转换为域对象
我想将来自客户端的Data Transfer Object
转换为域对象。 Aggregate's Constructor
只接受所需的字段,并且即使在创建Aggregate
(例如CreateAggregate
command
)时,也应该使用aggregate's
API
来传递其余参数。
但DTO
到Aggregate
映射代码变得有点凌乱:
if(DTO.RegistrantType == 0){
registrantType = RegistrantType.Person()
}
elseif(DTO.RegistrantType == 1){
registrantType = RegistrantType.Company()
}
//.....
//.....
var aggregate = new Aggregate(
title,
weight,
registrantType,
route,
callNumber,
)
//look at this one:
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
//..........
//..........
有一件事我应该提到的是,这个问题似乎并没有一个特定领域的问题。
我怎样才能减少而不让我domain internals leakage
这些if-else语句,并与被确认合计(不映射工具)不接受,可以invalide它的业务规则的值,并用具有无处不在语言适用?
请不要告诉我,我可以使用AoutoMapper
来伎俩。请仔细阅读最后一部分。'
谢谢。
一个典型的答案是将DTO
(实际上是一条消息)转换为Command
,其中命令将所有参数表示为特定于域的值类型。
void doX(DTO dto) {
Command command = toCommand(dto)
doX(command)
}
void doX(Command command) {
// ...
aggregate.Route(command.connectionType)
}
这是相当常见的为toCommand
逻辑使用像一个Builder
模式来提高代码的可读性。
if(DTO.connectionType == 0){
aggregate.Route(ConnectionType.InCity(cityId))
}
elseif(DTO.connectionType == 1){
aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId)
}
在这样一个情况下,策略模式可以帮助
ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType)
ConnectionType connectionType = f.create(DTO)
一旦你认识到ConnectionTypeFactory是一个东西,你能想到的关于建立查找表来选择合适的一个。
Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */
ConnectionTypeFactory f = lookup(DTO.connectionType);
if (null == f) {
f = defaultConnectionFactory;
}
那么你为什么不使用更继承
例如
class CompanyRegistration : Registration {
}
class PersonRegistraiton : Registration {
}
那么你可以使用,而不是继承你的if/else场景的
public class Aggregate {
public Aggregate (CompanyRegistration) {
registantType = RegistrantType.Company();
}
public Aggregate (PersonRegistration p) {
registrantType = RegistrantType.Person();
}
}
可以应用simmilar逻辑说setRoute方法或任何其他大的if/else情况。
另外,我知道你不想听,你可以编写自己的映射器(该aggegate内),其地图和验证它的商业逻辑
例如这个想法来自fluentmapper
var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>()
.ThatSets(x => x.title).When(x => x != null).From(x => x.title)
编写自己的映射器并不难,它允许这种规则和验证属性。我认为它会提高可读性
你的答案是完美的,但你知道'RegistrantType.Person()'返回枚举值0,而'RegistrantType.Company()'在这个特定情况下返回枚举值1。我没有把这个数字加入到汇总中的原因是为了避免泄漏域的内部。如果我在这个特定情况下创建一个工厂,工厂应该再次返回一个数字作为枚举标志。这听起来像是一个隐含的和贫乏的模型给我。任何建议? – Mohsen