0

I have a service, call it serviceA, which has a relational database. This service is old and bloated, modifications to the code are very difficult. But I also can't get rid of it, it's the heart of the system. Now I want to build serviceB. ServiceB is basically an extension of serviceA's capabilities, it has exactly the same domain. For this reason, and for reasons of performance, they both save their data in the same database. Both serviceA and serviceB can save an item of a certain type, call it itemC. The difference is that serviceB adds some metadata to itemC when it saves it.

Now here's the problem: there's a lot of business logic in serviceA around saving itemC. When saving an instance of itemC using serviceB this logic must execute and I have to know that the metadata is saved and that it's done quickly, no eventual consistency. I thought of three possible solutions:

  1. Having serviceA expose an endpoint which serviceB will use to save itemC and subsequently save the metadata. The problem is that this approach is potentially slow (there's no assurance that the metadata won't be saved a significant amount of time after itemC), complicated (I have to manage retries and consistency logic) and doesn't guarantee consistency.
  2. Having serviceB pass all necessary queries to save the metadata to serviceA and then having serviceA run them all in a transaction together with the query creating itemC. Potentially brittle because serviceA will have to run queries it knows nothing about.
  3. Extracting the itemC saving logic to a third service and have both services use it. That would require a lot of refactoring effort due to the condition of serviceA's code. It would also mean this third service would know about both services' tables, introducing coupling.

Any better ideas?

2
  • That would require a lot of refactoring effort due to the condition of service's A code time to stop and pay back the tech debt. You can not keep building a sandcastle and ask others how to ignore the mess. Any solution based on ignoring the current design instead of change or adapt it is likely to contribute to the tech. debt. So, not a solution at all.
    – Laiv
    Jun 29, 2021 at 14:43
  • 1
    I'm afraid it's too late for that. Some parts of the code are almost 20 years old and there is LOTS of it. The best that can be done is to stop extending it, isolate it from the rest of development and gradually retire it.
    – Johnny
    Jun 30, 2021 at 11:43

2 Answers 2

2

Another solution would be to expose an endpoint for serviceA which accepts also the new metadata (not the queries). And then change serviceA, that it would also persist those if provided.

Also you could modify version 3 and just extract the itemC saving logic of serviceA. Then serviceA uses it in its own transaction and serviceB uses it also, but including the metadata in its transaction.

As always, no silver bullet exists.
A third service would create a couplings. But it also would remove some. Because then, for itemC, both services would not be directly coupled to the database anymore.

If its a lot of refactoring effort, you may have to think about the alternatives. How likely is it, that you will have to change or add logic to the already tangled serviceA? It may be that starting to refactor the service may be cheaper, then to make it even more tangled.

Personaly, if "just adding some metadata" (i know, its never that easy) starts to become a real mindcrusher, i would start to think about refactoring sessions.

Also a sidenote (not really connected to your question :-) ):
There is a reason (in most cases multiple), why the old service is in such a bad condition. And if those reasons are not identified and stopped, its just a matter of time until the new service will run into the same problems. And a development team that defines the reason as "management / customer pushed to hard" makes it a little to easy in my opinion. In my experience in most cases it has to do a lot with the lack of knowledge (and not realising it) and the missing will to argue with the management.

1

Personally, I will go with the missing option:

  1. Since serviceB is an extension of ServiceA but with no reference to ServiceA to avoid circular dependency i.e that serviceB can do basically some necessary operations that ServiceA can do, e.g CRUD operations on "ItemC", you can then make ServiceB stateless by exposing an endpoint from ServiceB to serviceA. This way, let serviceA parse all what's needed(meta-data & maybe queries) to ServiceB it's like a "state transfer", and all ServiceB need to do is save the itemC. Good thing is ServiceB can be replaced easily, another thing is if you need to refactor ServiceA it will have no direct effect on serviceB, now you're on your way to "S" in SOLID principle.

At least this guarantees that serviceB is not tightly coupled to serviceA and vice-versa. While there can be some issues with this approach, like speed and maybe data exposure, it depends on your business model. but you're at least already creating the separation of concern.

2
  • Is this like #2 on my list, or am I misunderstanding?
    – Johnny
    Jun 30, 2021 at 11:49
  • Does seem like #2 but not really, check well. There is twist on which service is calling which
    – Wale
    Jun 30, 2021 at 14:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.