2

I'm confronted with a "microservices" architecture consisting of Spring Boot services fronted by a Vaadin Spring Boot application (with an API gateway in between), all written in Java. Communication between the frontend app and the services, and between services, is done using JSON over HTTP (I'm not calling it REST services here because the REST principles aren't strictly followed). JSON (un)marshalling is done using Jackson 2.

Since Java is a statically typed language, both sides need to have the exact same model: if you get data from a service, change a property, then post it back to the service, you will lose data if your model was missing a property since Jackson will have silently ignored the missing property in the deserialization process.

Because they need to have a dependency on the exact same version of the domain model, the services have to be built (whenever the model changes just a bit) and deployed together, making the architecture look more like a distributed monolith.

How is this typically avoided? When using a dynamic language like Javascript, all JSON properties will be retained even if the client is unaware of some of them. Therefore the service can change without impacting the client as long as it remains compatible.

  • 1
    Well, your JSON doesn't have to map precisely to a single-unit DTO. You could pass a collection of Key/Value pairs instead, for example. But yeah, generally the receiving end is going to expect some sort of well-defined data shape, and that's generally true whether you use Java or not. There are ways to be more flexible in your JSON deserialization to POCOs, if you need that kind of flexibility. – Robert Harvey Aug 4 '17 at 15:18
  • 1
    "they need to have a dependency on the exact same version of the domain model" Not really. You don't share a domain library because your two microservices have different models. If they had the same model, they would be the same service, wouldn't they? You have one service A which outputs whatever data it wants, and you have another entirely different service B with his own model which consumes data according to a certain format. If it so happens that A now provides a new value that B needs to consume, then you need to update B's model independently of A. – Vincent Savard Aug 4 '17 at 15:40
  • 1
    @Vincent If A produces a new model, then B should be able to consume the new model. The consumer can be backwards compatible, but any new additions to the producer need to be addressed. If all the services are Java, then sharing a common "protocol" library would be a good way to address that – cricket_007 Aug 4 '17 at 18:06
  • @cricket_007 Sure, but it doesn't have much to do with the domain model. You would probably share a client library which exposes some sort of data transfer object returned by the call to the other service. You should definitely not share a domain model library, because each microservice should have its own domain. – Vincent Savard Aug 4 '17 at 18:15
  • 2
    The issues you describe have nothing to do with the design of the language you are using and everything to do with the design of the application. – JimmyJames Aug 4 '17 at 20:12
1

Since Java is a statically typed language, both sides need to have the exact same model

Not really.

if you get data from a service, change a property, then post it back to the service, you will lose data if your model was missing a property since Jackson will have silently ignored the missing property in the deserialization process.

Because we usually bind the service data model to DTOs. That's it.

Recently, I have started to play with Tolerant Readers instead of data bindings. 1

Mapping the message to a tree structure of nodes is the key to make readers tolerant. Like in Javascript, the new fields won't be ignored. The reader will just remain unaware of them.

Not a silver-bullet of course, but the goal here is to shrink the coupling surface to the minimum.2

Because they need to have a dependency on the exact same version of the domain model, the services have to be built (whenever the model changes just a bit) and deployed together, making the architecture look more like a distributed monolith.

Few things here.

The coupling

Assume that there is going to be coupling. Allways. Somewhere. Don't look for the coupling zero because it doesn't exist. Our battle against coupling is reduced to: looking for the right place and keeping it as loosely as possible.

In the line with these arguments, we have to find out how much coupling/decoupling are we willing to afford.

At this point don't be dogmatic, be pragmatic, because we are not managing an architecture of the scale of Amazon or Netflix. Neither our needs for decoupling are the same.

Sharing data models

Shared data models are the root of the evil. Ideally, we only map/bind/read the essential and we will never allow our bindings to go beyond the anti-corruption layer. In other words, if our DTOs are reaching upper layers, the problem is not Java. The problem is the lack on abstraction layers.

It's utterly important to keep domain data models unaware one another. This's the key of the bounded contexts and the SRP they represent.

Deployments

This is why some API providers do versioning. For a while, the old version coexist with the new one and consumers are encouraged to shift progressively from one to another.

Ultimatelly, we are speaking about Microservices here, we should be able to deploy as many as we need and as many versions as we deem appropiate. Any time.

If we can't, we are right saying that ...

the architecture looks more like a distributed monolith.


1: Not totally I have to say. I still bind messages to DTOs but only those I consider unlikely to change or relatively small.

2: Take in account that every new DTO we map contributes to more coupling. Let's say that DTOs increase the coupling surface.

  • Thanks for mentioning Martin Fowler's TolerantReaders article. – herman Aug 5 '17 at 1:13
  • Regarding sharing of data models (same comment as above): the Vaadin front-end application deals with data from different services, and at some point nearly every property will be shown somewhere in the GUI, so the exposed data model is the same as the one used for reading data from MongoDB. I guess the issue here is that the services don't have their own GUI. – herman Aug 5 '17 at 1:25
  • In addition, the models are shared between services because they (partly) need the same data. Consider an employee management (HR) application, a planning tool and a payroll calculation tool. Each could be their own services but the employee data would need to be shared, e.g. all three will need to know when the employee is on sick leave, working part time etc. The planning service may not need to know the employee's birthdate but the other two will. Having largely the same models duplicated, and then needing to write mapping code that does 1-on-1 mapping for the most part, sounds tedious. – herman Aug 5 '17 at 1:36
  • Having largely the same models duplicated, and then needing to write mapping code that does 1-on-1 mapping for the most part, sounds tedious. This depends on what I have commented. How much coupling/decoupling are you willing to afford? It's commonly accepted that having duplicated code across the differents services is preferable over coupling. However, the only big advantage of Microservices architectecture is independency. Absolute independency from each other. In all the senses (SDLC, management, governance, etc). – Laiv Aug 5 '17 at 8:18
  • If we can not ensure each MS independency, the strategy starts to fail. Several developments starts as Microservices and ends as monolith. Has been proven that it's way more difficult to start a Microservices architectecture from scratch than splitting a monolith. In your case, worth nothing to review the boundaries of each bounded context, looking for missing bounded contexts or better IPC protocols. I could understand tour pain if all your IPC is based on Http and REST. Consider alternative protocols and messaging models. – Laiv Aug 5 '17 at 8:27

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.