When working with JSON data or similar serialization formats, I’ve come to appreciate the value of model classes instead of just using plain objects. So when I find myself deserializing records, processing them and serializing them again, e.g. for transmission via HTTP or local storage, I might create a simplistic implementation at first:
Already dangerously repetitive, this gets unwieldy quickly as we add more properties and create additional models. It also seems kind of silly to de- and recompose the underlying data structure when (de-)serializing. Thus we might cut out the middleman by storing the original data object and selectively delegating property access from our model instance to that object - essentially keeping data separate (hardly a new idea, of course) and layering a facade on top:
This is the read-only version; filling in setters should be fairly
straightforward if you need them. You might also want to validate the data
object (e.g. using joi, though I’m partial to
declepticon)
or avoid mutability issues by cloning that object within both the constructor
and #toJSON
.
Next we can extract common boilerplate into a base class:
This is starting to look more declarative, without obscuring what’s actually going on. Depending on the number and complexity of your models, it might still be too repetitive though, making it arduous for readers to grok the code; consider a simple example with multiple models. In that case, you might want something that looks more like this:
Except now we have to make our Record
base class generate those property
accessors, meaning we have to engage in some light metaprogramming – often an
indicator that you might wanna reconsider, but let’s see what that might look
like. Delegating property access essentially means our model class
acts as a proxy – except I couldn’t use
Proxy
for compatibility reasons, ergo DIY:
While that works nicely for primitive data structures, if we want models referencing other models we’ll need to support nested data transformations:
That requires slightly adjusting our base class:
I’m pretty sure this already exists as a library or within some framework (pointers are welcome in the comments), but I just needed a no-frills approach and writing that myself proved easier than trawling the web; YMMV.