Search code examples
javarestweb-servicesdrypojo

How to avoid duplicate POJOs in different web services, Java


I have a webapp with multiple Spring services (they each have their own ear and web-controllers) and they talk to each other via REST calls. Some of the different services use the same data POJOs. The existing code just has duplicates of those data objects in different services.

For example, my /users service has a myApp.users.UserData object, and my /emails service calls /users/{userId} and holds the result as a myApp.emails.UserData object. Both of these objects are identical.

The problem here is I have to keep myApp.emails.UserData and myApp.users.UserData in sync, since in reality they represent the same info and are meant to be the "same" class. Say I update the name of a field in emails.UserData, I better remember to update it in users.UserData, otherwise things will break.

I know I could make a shared dependency called something like SharedDataObjects, define myApp.sharedDataObjects.UserData there, and just have both versions refer to that. For some reason, though, my gut feeling is that this is not a good solution... (maybe it is though?)

Are there any better ways of approaching this issue? Is there something fundamentally wrong with the way the webapp structured, and if so how could that be addressed?


Solution

  • It's always tempting to spot duplicated (or nearly duplicated) code and try to eliminate the duplication. An obvious path in that direction is to make a shared library (or module) and have different services (or applications) depend on it.

    Doing so, however, increases coupling between services/applications/modules, which has its own set of drawbacks in many contexts. Especially in a microservices architecture*, that kind of coupling often leads to headaches. It can even lead to loss of the value of using microservices* in the first place.

    If two services* are supposed to be independent but integrated, they have an integration protocol of some kind. For REST services, for example, that's almost always HTTP and JSON**. By introducing a shared library, you have coupled them in a binary way, separate from (and more binding than) HTTP and JSON. Not a good situation; experience has taught many of us that painful lesson.

    Instead, focus on the public interfaces that each service exposes, and use appropriate versioning to evolve those interfaces when needed. Don't worry about some duplicate-looking classes, especially if they're anemic POJOs; that's a minor concern compared to a tightly-coupled set of services that are supposed to be independent.

    Not that shared code libraries are inherently bad, they're not and they have true value in many ways. Rather, my point is you need to make sure each service maintains its own definition of what important things are, so that each can remain as independent as possible - and evolve independently as much as possible.

    By the way, this is somewhat related to the concept of Bounded Contexts; you might want to read more about it if you're working with microservices*.


    *or whatever you call your architecture of independently-managed services/modules/apps

    ** Could also be some kind of messaging/queueing platform, as another example