Remix.run Logo
delusional a day ago

> Lack of layer separation becomes a problem in the long term. When you're just starting out, it may seem like overengineering, but it isn't

I actually agree, but you're setting of a false dichotomy. I do believe in strong layering at the interfaces, for exactly the reasons you line up. What I don't believe in is what I might call "struct annotation based parsing" at those interfaces.

Typically, you don't want to pass DTO's around your code. Usually, you take in that struct, and then immediatly have some code to poke it into the appropriate places in your actual data structures. It's very often much easier to simply take a well structured but more direct and generic interpretation of the input data, and write the code to poke it into the correct places directly.

It is not that you should define your inputs separately from your internal data storage. It's that the specification of your input structure shouldn't exist as a struct, it should exist as the consequence of your parsing code.

> When you're just starting out, it may seem like overengineering, but it isn't

It's a real shame that the internet has driven us to assume everybody is a novice.

kgeist a day ago | parent [-]

>It's a real shame that the internet has driven us to assume everybody is a novice.

Sorry, English is not my native language. I didn't mean to say you're a novice.

>Usually, you take in that struct, and then immediatly have some code to poke it into the appropriate places in your actual data structures

>It's that the specification of your input structure shouldn't exist as a struct, it should exist as the consequence of your parsing code.

>It is not that you should define your inputs separately from your internal data storage. It's that the specification of your input structure shouldn't exist as a struct, it should exist as the consequence of your parsing code.

Can you give an example?

delusional a day ago | parent [-]

> Can you give an example?

Sure. I like taking Jackson (the Java library) as an example, since it actually supports both models. The way I've seen it used mostly is with jackson-databind. Here you define classes and annotate the fields with data that tells the library how to marshall them to/from json. Superficially, I find that similar to how Go or SerDe (from rust) suggests you handle that. In that programming model, I agree it makes total sense to declare some classes separately from your core structures, for all the reasons we've talked about.

The other model Jackson has is what they call the Jackson Tree Model. In this model you get back a representation of a Json Object. From that object you can get fields, those fields can themselves be objects, arrays, or immediate. It's an AST if you're a compiler person.

The first model might lead to code like this:

    public class Person {
        @JsonProperty
        String name;
        @JsonProperty
        String phoneNo;
    }
Usually, the annotations wont be able to fully specify the constraints of your code, so you'll see usage code like this:

    if(personDto.phoneNo.length() != 10) return Http(400, "Phone number must be 10 chars");
    person.phone = personDto.phoneNo
With the Tree Model you'd instead get a representation of the raw JSON from the client and pull out the fields you care about yourself:

    var phoneNoObj = jsonBody.get("phoneNo");
    if(phoneNoObj == null) return Http(400, "Phone number is required");
    var phoneNoStr = phoneNoObj.asString()
    if(phoneNoStr == null) return Http(400, "Phone number must be a string");
    if(phoneNoStr.length() != 10) return Http(400, "Phone number must be 10 chars");
    person.phone = phoneNoStr;
Notice that we are now just doing a single application specific parse of the json, and while we were at it we also got to surface a bunch more relevant errors. The Jackson Tree model is obviously pretty inefficient, but there are ways to implement it that makes it more efficient too.
kgeist 20 hours ago | parent [-]

In Go, you can marshal JSON to a map, so you can extract fields manually, too.