Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I'd go with parsing the BSON documents and move the parsing logic to a factory.</p> <p>First define a factory base class, which contains a builder class. The builder class will act as the DTO, but with additional validation of the values before constructing the domain object.</p> <pre><code>public class TransportFactory&lt;TSource&gt; { public Transport Create(TSource source) { return Create(source, new TransportBuilder()); } protected abstract Transport Create(TSource source, TransportBuilder builder); protected class TransportBuilder { private TransportId transportId; private PersonCapacity personCapacity; internal TransportBuilder() { } public TransportBuilder WithTransportId(TransportId value) { this.transportId = value; return this; } public TransportBuilder WithPersonCapacity(PersonCapacity value) { this.personCapacity = value; return this; } public Transport Build() { // TODO: Validate the builder's fields before constructing. return new Transport(this.transportId, this.personCapacity); } } } </code></pre> <p>Now, create a factory subclass in your repository. This factory will construct domain objects from the BSON documents.</p> <pre><code>public class TransportRepository { public Transport GetMostPopularTransport() { // Query MongoDB for the BSON document. BsonDocument transportDocument = mongo.Query(...); return TransportFactory.Instance.Create(transportDocument); } private class TransportFactory : TransportFactory&lt;BsonDocument&gt; { public static readonly TransportFactory Instance = new TransportFactory(); protected override Transport Create(BsonDocument source, TransportBuilder builder) { return builder .WithTransportId(new TransportId(source.GetString("transportId"))) .WithPersonCapacity(new PersonCapacity(source.GetInt("personCapacity"))) .Build(); } } } </code></pre> <p>The advantages of this approach:</p> <ul> <li>The builder is responsible for building the domain object. This allows you to move some trivial validation out of the domain object, especially if the domain object doesn't expose any public constructors.</li> <li>The factory is responsible for parsing the source data.</li> <li>The domain object can focus on business rules. It's not bothered with parsing or trivial validation.</li> <li><p>The abstract factory class defines a generic contract, which can be implemented for each type of source data you need. For example, if you need to interface with a web service that returns XML, you just create a new factory subclass:</p> <pre><code>public class TransportWebServiceWrapper { private class TransportFactory : TransportFactory&lt;XDocument&gt; { protected override Transport Create(XDocument source, TransportBuilder builder) { // Construct domain object from XML. } } } </code></pre></li> <li><p>The parsing logic of the source data is close to where the data originates, i.e. the parsing of BSON documents is in the repository, the parsing of XML is in the web service wrapper. This keeps related logic grouped together.</p></li> </ul> <p>Some disadvantages:</p> <ul> <li>I haven't tried this approach in large and complex projects yet, only in small-scale projects. There may be some difficulties in some scenarios I haven't encountered yet.</li> <li>It's quite some code for something seemingly simple. Especially the builders can grow quite large. You can reduce the amount of code in the builders by converting all the <code>WithXxx()</code> methods to simple properties.</li> </ul>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload