3 ms·
> I guess this is the "by hand" mapping that the article concludes with Not "the", "a". One of the comments under the OP itself links to another blog post [0]
by Twisol 6y ago
> I guess this is the "by hand" mapping that the article concludes with
Not "the", "a". One of the comments under the OP itself links to another blog post [0] describing an alternative to explicit DTOs, which I personally prefer.
The point isn't just to pick some other solution and be done with it; the point is to understand the problem in the first place.
[0] http://www.ballofcode.com/ddd/boundaries/serialization/2016/02/20/avoid-serialization-objects.html http://www.ballofcode.com/ddd/boundaries/serialization/2016/...
> It's incredibly easy to tweak JSON to allow it
Sure, but that was just an example of a general class of coupled-representation problems. As discussed elsewhere in this thread, the choice of JSON isn't even essential to the problem being discussed.
Also, even though (or precisely because) JSOG is 100% JSON, the fact remains that after your JSON parser finishes reading your JSOG document, you still have to hook up all the cycles. Either you do this by hand (per the article), or you wrap it up into parser-level knowledge (which breaks somewhat from the "100% JSON" intent).
- stickfigure 6y agostatic JObject CreateMessageRepresentation(Customer customer) { return new JObject( new JProperty("customer", new JObject( new JProperty("name", customer.Name), new JProperty("address", new JObject( new JProperty("street", customer.Address.Street), new JProperty("zipCode", customer.Address.Zip), new JProperty("town", customer.Address.City) ) ) ) ) ) } Yuck. Just make CustomerData and AddressData classes, even if you only use them for that one API response. And even if you have ten other versions of CustomerData and AddressData for ten other methods. You get type safety and your tests refactor nicely. @Value static class AddressData { String street; String zipCode; String town; } @Value static class CustomerData { String name; AddressData address; } @Value static class Message { CustomerData customer; } public Message createMessage(Customer customer) { final Address addy = customer.getAddress(); return new Message(new CustomerData(customer.getName(), new AddressData(addy.getStreet(), addy.getZip(), addy.getCity()))); } You could format this nicer, and adding some constructors would help, but at least the typechecker is doing work for you.
- Twisol 6y agoAdapting your example to a component of a real project I'm working on: JsonParser<Customer> customerP = productP . field("customer", productP . field("name", stringP), . field("address", productP . field("street", stringP) . field("zipCode", stringP) . field("town", stringP) . map(uncurry3(street -> zipCode -> town -> new Address(street, zipCode, town)))) . map(uncurry2(name -> address -> new Customer(name, address)))); This is a parser, not a serializer, but hopefully it's clear how this approach can be applied in the other direction. (I still have the analogous code on a branch somewhere, but the parsing logic needed a cleanup more, and sooner.) Some of the uncurried function stuff can be cleaned up with dedicated wrappers for `BiFunction` and whatnot. And of course, the address parser could be extracted out if we want to unit test it separately. I don't find the code I give above to be any worse than the code you gave (for that matter, I don't have the same "yuck" reaction to the C# example, either). I much prefer not needing extra data types that only serve to configure analogous translation code. We can keep going back and forth, addressing the concerns we have about each other's approach, but it ultimately comes down to preference.
- BlackFly 6y agoWell you have chosen an example that is little more than an API object in the first place. class IdealGas implements EquationOfState { private final double gamma; public IdealGas(double gamma) { this.gamma = gamma; } public double energyDensity(double pressure, double internalEnergy) { return (1 + gamma) * pressure * internalEnergy; } } Why create a separate type over this class which is just a projection of its data? You can just use a JSONObject as the API object. You are already going to need some special tricks to deal with the union with other EquationOfStates on top of some out of band type field to designate which class is to be used. You will have the same sort of boiler plate in either case. Either a `public EquationOfStateData getEOSData()` or a `public JSONObject getJSON()`. In one case you use type safety and the deserializer provides your validation messages but should still do some custom validation on top to handle mismatched unions, in the other case you perform the type check (ordinarily done by using method like `json.getDouble()` and get to give custom messages. Choose your poison, they really aren't all that different.