6

When breaking up a large monolithic application with a monolithic RDBMS into a service-oriented architecture with many databases, how do you deal with the breaking of data integrity?

I have a large monolithic application. My team has agreed that it's beneficial to break up this monolith into domain-specific services and embrace more of a service-oriented architecture. With our monolith, we have a huge RDBMS database. The team is split as to whether it's appropriate to carve out domain-specific tables into their own databases. Some folks are concerned about breaking referential integrity and the risk that that brings. However, others are OK with this risk.

Does anyone have experience with this problem and can share some advice? One of my goals is to facilitate the ability to scale horizontally (add more DB servers) as opposed to vertically (beefing up a single huge DB server). At some point in the future, I could envision that certain services may be better suited to a document DB aka NoSQL data store than an RDBMS, and certainly, traditional RDMBS-based integrity would be challenging there. (So approaches like storing the tables in a separate schemas on the same database probably won't be appropriate.)

6

That referential integrity is a concern at all suggests that there are implicit dependencies between the domains. One thing that I think people who have drunk the microservices kool-aid often misunderstand is that these kinds of dependencies and the issues related to them do not magically disappear once you break down the monolith. It's good that people are concerned and if you are committed to this path, you will want to make sure those concerns are addressed.

If there is really tight coupling between two or more domains from a business perspective, you might want to consider whether they should really be separated into separate DBs. It might not be worth the trouble.

If you do need to maintain integrity between two entities, a couple things might help:

  1. Use surrogate keys always. There should be no reason to change meaningless keys and if your keys never change, you avoid huge swath of RI problems.
  2. Never delete keys and use 'soft' deletes instead. Only adding rows and never updating them is also helpful. At the very least you will know what things meant when they were linked.
  • Thanks for your perspective. I wouldn't say there is tight coupling between our services, but there are certainly dependencies between them (or else they would be useless). For us, continuing to vertically scale a gigantic single DB server isn't a viable approach. While RDBMS integrity is good, it doesn't mean that the system is perfect. I tend to agree more with Mike L. – Andy Jun 6 '17 at 21:41
3

I would be careful about thinking of sharing a database with another service. Every time a database change is made, you now how to verify that the change does not affect any other service that uses that database. You are back to doing monolithic releases where tests are being run against every service to see if you can release. This will be compounded if the service is owned by another team as database changes now require cross-team communication which slows down development and creates issues of scheduling etc.

One of the benefits of SOA and microservices is that the boundaries are at the API level which remains relatively stable across releases. This allows a microservice to individually release without needing to test the whole system. Having a separate database per service allows this type of development to proceed.

In terms of referential integrity, it will depend on your domain. If referential integrity is mission critical to your business, then I might propose finding less granular places to split up your monolithic. For example you might have one service that represents all the domain logic and relationships of core domain entities. You then might have supporting services, like an email service, an authentication service, etc. Your domain service might not be "micro" but if referential integrity is important, don't risk it by going too granular in your service design.

If you relax your referential integrity to be eventually consistent/application maintained, then a whole new world opens up. You can have services that are the system of record for certain domain concepts/entities and then denormalize the data for fast queries in other parts of the system. This sharing of data takes time to propagate but for most domains that is not a problem. You have to decide if it is ok to show or perform validation against this eventually consistent data.

If you have business rules that would be have to be performed by going to multiple services to be validated and performed than your domain might not be a good a choice for SOA. If instead your business represents workflows, where each service performs some step in a larger business flow, this would be a much better use case. I have found that the best place to break up a monolith is at bounded contexts. Sometimes these services end up still being pretty large, but that is the way the domain is modeled. It will be painful if you go too granular as you will be generating a large number of network requests to service queries or perform data manipulation that required a few joins in your monolith. You also won't have referential integrity as you could make a call to validate a record exists in another service and have it be deleted right after the call completes.

Edit:

One way to maintain eventual consistency between records is to have a process that looks for orphaned records or that links records together. You could provide users a place to clean up these orphaned records in the UI to allow them to take action. At query time you can detect issues and let the user know as well to allow them to fix the issue.

You can use tools like rabbitmq or Kafka as a mechanism for services to share data that will be eventually consistent.

As an example I might have an eventually consistent store of user's full names from the user service in my service so that I don't need to query the user service constantly to display the full name. I might listen to events coming through kafka from the user service in order to build up my eventually consistent store. Now full name lookups are fast in my service at the cost of data duplication.

The integrity here will be strong as user's full names probably won't change that often. Other datasets that change a lot might have less integrity as the queue/kafka might be behind.

Each service that owns a record is responsible for the integrity of that record. The relationships with other records will have to be more flexible. As a consumer service of a record from another service, you will have to listen and adapt to changes that occur to the record inside its owning service since it has the final say to the current state of the record.

  • I favor the separate data store per service / relaxed integrity approach that you mention here. I'm not of the opinion that referential integrity is mission critical for us, although others on my team may disagree. Do you have any links on how to ensure that integrity is eventually maintained? – Andy Jun 6 '17 at 21:44
  • Updated my answer to talk about ways integrity can be maintained. – Mike L. Jun 7 '17 at 16:45
1

First of all, there are apparently multiple ways you could split the monolith. If you have mission-critical constraints with corresponding entities that end up being in different services, you identified service boundaries incorrectly.

There are certain characteristics that you want your services to possess. Apparently your services should have low coupling. You don't want your monolith to become a distributed one. You don't want your whole system to go down because of a bug in some service. You want your services to have high cohesion: if you need to make some closely related changes in your source code you want all the code to reside in a single place. These two are probably the most important traits. All the rest can be concluded from them. Those are high service autonomy, communication via events, decentralized data and service choreography instead of orchestration.

Well, that sounds good and fine, but what are the concrete steps to take? What I finally came to is the following. I define the business capabilities that my system has, it is basically its higher-level responsibilities. But don't confuse organizational structure with business-capabilities. Ideally, they coincide, but this is not the case more often than not. So write down your higher-level business-capabilities. There should be about 5-7 of them. Not more than 10, I think. Then delve deeper in each of them. What loosely-coupled responsibilities do they consist of? Do not think about programming when you do that. You just decompose you problem space. Business-capabilities you end up with will reflect your domain, so they intrinsically will be loosely coupled. No constraints will be violated. There is a metaphor that helps me with that decomposition: I conjure how my current domain looked like (or would look like) a hundred years ago. What are really mission-critical constraints? What constraints are just a result of domain misunderstanding? I bet your domain is more eventually-consistent-like than you think.

After you are done with this capabilities decomposition, you can map this result to solution space. I call solution-space entities that correspond to problem-space capabilities "business-services". It is the logical boundary where business-capability resides. There are business-rules, business-processes, people involved in them and making specific decisions, applications used by these people and business-data operated upon people.

So after that you finally decide what you want to automate. So your technical, SOA-services would have 1:1 relation with your business-service.

Here is an example of what I wrote about.

So following this approach there is no such an issue like breaking data integrity.

Hope that helped.

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.