0

I'm having problem deciding which of these 2 structures to use from my DTOs:

Option 1 is:

@Data
class Lines{
  private final Line[] lines;
}

@Data
class Line{
  private final String[] specifiers;
  private final Set<String> container;
}

Which can be used as this:

//Single
Lines lc1 = new Lines(new Line[]{
    new Line(new String[]{"m:123"}, new HashSet<>(Arrays.asList("1", "2", "3")))
});

//Composite
Lines lc2 = new Lines(new Line[]{
    new Line(new String[]{"m:161", "l:1"}, new HashSet<>(Arrays.asList("1", "2", "3"))),
    new Line(new String[]{"m:161", "l:2"}, new HashSet<>(Arrays.asList("1", "2", "3")))
});

and when serialized produce these jsons:

//Single
[
    {
        "specifiers": ["m:591"],
        "container": ["1","2","3"]
    }
]

//Composite
[
    {
        "specifiers": ["m:161", "l:1"],
        "container": ["1","2","3"]
    },
    {
        "specifiers": ["m:161", "l:2"],
        "container": ["1","2","3"]
    }
]

OR Option 2 which is:

interface ComponentLine{}

@Data
@Accessors(fluent = true)
class Line implements ComponentLine {
  private final String specifier;
  private final Set<String> container;
}

@Data
@Accessors(fluent = true)
class CompositeLine implements ComponentLine{
  private final String specifier;
  private final Set<ComponentLine> lines;
}

which can be used as this:

//Single
Line l1 = new Line("m:123", new HashSet<>(Arrays.asList("1", "2", "3")));

//Composite
Set<Line> lines = new HashSet<>();
  lines.add(new Line("l:1", new HashSet<>(Arrays.asList("1", "2", "3"))));
  lines.add(new Line("l:2", new HashSet<>(Arrays.asList("1", "2", "3"))));
  CompositeLine cl1 = new CompositeLine("m:161", lines);

and when serialized produces these jsons:

//Single
{
    "specifier": "m:591",
    "container": ["1","2","3"]
}

//Composite
{
    "specifier": "m:161",
    "lines": [
                {
                    "specifier": "l:1",
                    "container": ["1","2","3"]
                },
                {
                    "specifier": "l:2",
                    "container": ["1","2","3"]
                }
             ]
}

So far, when looking into both options, I can see some benefits of both approaches:

  • Option 1 is more unified meaning I can pass either composite or single line in same structure - possibly having easier code to write for processing and deserializing. Also, I don't need to have separate deserialization checks like in option 2 (if object has property lines -> deserialize as composite, otherwise deserialize as simple line).
  • Option 2 is more compact, since I don't have to repeat each time same data (like in Option 1 where I had to repeat 2 times string "m:161"), and since this is DTO, size does matter.

Anyone has more experience with this kind of DTO-s, and what would be more beneficial out of this 2 things I could find?

  • 3
    There's no best practice here, just the most "fit for your purpose". Since that's the information we lack, we can't advise you on that. So which option fits your purpose the best? – Berin Loritsch Mar 28 at 12:14
  • I would create a prototype and see what works better for you. Sometimes it's much easier to decide when you have some real code. – Dan Wilson Apr 2 at 15:25
3

There are a few factors that cover deciding which DTO objects are best for your purpose:

  • Fitness for purpose: What most appropriately characterizes the data you are sending, and for the purpose that you are sending it? This should be your primary concern.
  • Ease of consumption: How easy is it to produce or consume this data? This should be a major concern.
  • Data efficiency: How many bytes does it take to pack the same information? If you have two formats that are equally fit, then you can use this criteria as a tie breaker.

Bottom line is that you'll have to actually see the format with real data in it. That will help you get a feel for how well it will work. That process will also help you figure out how easy it is to generate the data. Afterwards, you can see what your average message size will be.

Considering I don't know the specifics about your data transfer requirements, all I can do is give you general guidelines to help you choose.

  • Just to add one more point on your list - I just wanted to use protobuff and it seems that Option 2 is almost impossible to implement properly, since there is no easy way to have polymorphic behavior. – Bojan Vukasovic Apr 2 at 15:14
  • @BojanVukasovic, I think that falls under "Ease of Consumption" – Berin Loritsch Apr 2 at 16:44
  • Yes, but in my case (since other service expects protobuf), 2nd point is actually defining 1st one. Fitness for purpose is option 2, but since it cannot be expressed in protobuf I think I will have to fallback to option 1. – Bojan Vukasovic Apr 3 at 13:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.