2016-09-21 1 views
0

Ich versuche, eine allgegenwärtige Sprache auf meine Domänenobjekte anzuwenden.So konvertieren Sie ein DTO zu Domänenobjekten

Ich möchte eine Data Transfer Object von einem Client in das Domain-Objekt konvertieren. Die Aggregate's Constructor akzeptiert nur die erforderlichen Felder, und die restlichen Parameter sollten unter Verwendung von aggregate'sAPI auch dann weitergegeben werden, wenn die Aggregate erstellt wird (z. B. CreateAggregatecommand).

Aber der DTO zu Aggregate Mapping-Code wird ein wenig chaotisch:

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) 
} 
//.......... 
//.......... 

Eine Sache, die ich erwähnen soll, ist, dass dieses Problem nicht ein domänenspezifisches Problem scheint.

Wie kann ich reduzieren diese If-Else-Anweisungen ohne meine domain internals leakage zu lassen, und mit sicher zu sein, dass die Aggregat (kein Mapping-Tool) keine Werte annehmen, die es Geschäftsregeln invalide kann, und mit dem allgegenwärtigen mit Sprache angewendet?

Bitte sagen Sie mir nicht, dass ich AoutoMapper verwenden kann, um den Trick zu tun. Bitte lesen Sie den letzten Teil sorgfältig.

Vielen Dank.

Antwort

2

Eine typische Antwort wäre, die DTO (die eigentlich eine Nachricht ist) in eine Command zu konvertieren, wo der Befehl alle Argumente als domänenspezifische Werttypen ausgedrückt hat.

void doX(DTO dto) { 
    Command command = toCommand(dto) 
    doX(command) 
} 

void doX(Command command) { 
    // ... 
    aggregate.Route(command.connectionType) 
} 

Es ist ziemlich üblich, dass die toCommand Logik Verwendung so etwas wie ein Builder Muster der Lesbarkeit des Codes zu verbessern.

if(DTO.connectionType == 0){ 
    aggregate.Route(ConnectionType.InCity(cityId)) 
} 
elseif(DTO.connectionType == 1){ 
    aggregate.Route(ConnectionType.Intercity(DTO.originCityId,DTO.DestinationCityId) 
} 

In Fällen wie diesen kann das Strategie-Muster

ConnectionTypeFactory f = getConnectionFactory(DTO.connectionType) 
ConnectionType connectionType = f.create(DTO) 

helfen Einmal, dass Sie erkennen, dass ConnectionTypeFactory eine Sache ist, können Sie darüber nachdenken, Lookup-Tabellen bauen, die richtige zu wählen.

Map<ConnectionType, ConnectionTypeFactory> lookup = /* ... */ 

ConnectionTypeFactory f = lookup(DTO.connectionType); 
if (null == f) { 
    f = defaultConnectionFactory; 
} 
+0

Ihre Antwort ist perfekt, aber Sie wissen 'RegistrantType.Person()' kehrt Enum-Wert von 0 und 'RegistrantType.Company()' kehrt Enum-Wert von 1 in diesem speziellen Fall. Der Grund, warum ich die Nummer nicht in das Aggregat übertragen habe, war, die Interna der Domain nicht zu verlieren. Und wenn ich eine Fabrik in diesem speziellen Fall erstelle, sollte die Fabrik wieder eine Nummer als enum flag abgeben. Und das klingt für mich wie ein implizites und anämisches Modell. – Mohsen

1

Also, warum nicht Sie mehr Vererbung

zum Beispiel

class CompanyRegistration : Registration { 

} 

class PersonRegistraiton : Registration { 

} 

dann können Sie die Vererbung statt Ihrer if/anderes Szenario

public class Aggregate { 
    public Aggregate (CompanyRegistration) { 
    registantType = RegistrantType.Company();  
    } 

    public Aggregate (PersonRegistration p) { 
    registrantType = RegistrantType.Person(); 
    } 


} 

können Sie Wenden Sie eine ähnliche Logik für eine setRoute-Methode oder andere große if/else-Situationen an.

Auch ich weiß, dass Sie es nicht hören wollen, können Sie Ihre eigene Mapper (innerhalb des aggegate) schreiben kann, dass die Karten und validiert es Business-Logik ist

zum Beispiel diese Idee von fluentmapper

kommt
var mapper = new FluentMapper.ThatMaps<Aggregate>().From<DTO>() 
        .ThatSets(x => x.title).When(x => x != null).From(x => x.title) 

Es ist nicht zu schwer, einen eigenen Mapper zu schreiben, der diese Art von Regeln zulässt und Ihre Eigenschaften validiert. Und ich denke, es ist die Lesbarkeit verbessern