One of the key principles of the design of many of the different API platforms I have built recently was to separate the model exposed to users from what is stored. I have and continue to be a huge fan of using Protobuf to model the internal messages consumed in systems. Using POJO classes and Jackson Json serialisation provides a great choice for the seialisation too.
Irrespective of the tech used separating these two layers has been a mandatory feature of systems I have built for a while. The key thing is creating flexibility of representation while also having a stable storage model.
As part of a redesign of the external API for Upgrade I reviewed the features of the model. I had a shopping list of features I wanted to add. As many times before I started refactoring the representation while rerunning the tests to ensure I didn't break the representation published for the API. I started noticing a few patterns of code that needed cleaning up. These models are after all the oldest code in that project.
Over time the project had been refactored a number of times and this layer of the code had become more encapsulated. The up shot of this is that new abstractions introduced in other places had not been applied in these old objects. The changes all had the same basic pattern:
"Don't mix layers of abstraction"
When the project first started many of the messy features had been cleaned up. While awesome tools like IntelliJ will find some common refactoring this was a more subtle layering refactoring.
To try and avoid this again I came up with a new rule for these kinds of classes where if I find myself writing helpers, statics etc. I will add a TODO to review later. This sort of problem can often occur earlier in a project especially when the general structure and repeted functions haven't been identified yet.
Remember to watch out for the mixed layers of abstraction as a clue. Happy coding.