Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designing java project for monoliths and microservices at same time

I would like to know how you divide project modules in java for monolith with possibility of transforming modules to micro-services later?
My personal naming looks like this:

com.company.shopapp.product
...product.domain (ddd, services, repositories, entities, aggregates, command handlers - everything with package scope)
...product.api (everything with public scope)
...product.controller (CQRS endpoints for commands in web perspective - (package scope))
...product.query(CQRS - package scope)

com.company.shopapp.sales
- domain
- api 
- controller
- query 

What we have here is basically product management context and sales context as packages.

Modules communicate each other using public interfaces (api package) only. In my project I use "..api.ProductFacade" to centralize communication points.

When my "sales" module grow i will turn it into microservice by implementing "..api.ProductFacade" interface as a "rest" or "soap" client and on the other side I will create Endpoint/RestController based on ProductFacade interface. Package "com.company.shopapp.product.api" will be transformed into extended library and added to both projects.

Edit: I can achive this out of the box using @Feign library. https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html#spring-cloud-feign-inheritance

The whole idea feels nice, but maybe you have better way to design project and ensure that breaking it into micro-services will not break whole application.

like image 716
John Tribe Avatar asked Nov 28 '17 09:11

John Tribe


People also ask

Which AI assisted tool help transforms Java monolithic applications into microservices?

IBM® Mono2Micro is an AI-based semi-automated toolset that uses novel machine learning algorithms and a first-of-its-kind code generation technology to assist you in that refactoring journey to full or partial microservices, all without rewriting the Java application code and the business logic within.


2 Answers

I think your module structure is good. But I would suggest you create a real 'multi module' project (link). This way, using code from another module will generate a compile-error. This will help you to keep your good intentions!

In order to do this, you'll have to split each module in a private (implementations) and public (api, only interfaces) module (By doing this, you don't need an 'api'-package). An implementation module can depend on any public-module, but not a private-module.

If you wire your application together in the private module, with dependency injection, the private modules will have no 'internal' dependencies! The private modules will not have any 'compile-time' dependencies, only 'runtime' dependencies.

Here quick module dependency graph: enter image description here

I hope you find this usefull!

Edit: You will need an extra module only to bootstrap the application!

like image 186
Tom Van Rossom Avatar answered Sep 30 '22 15:09

Tom Van Rossom


TLDR: Think components and modules separately and establish their "touch points"

Modules, as in your example, look like cross-cutting structure, which corresponds well enough to the recommended microservice practice. So, they can all be parts of a single microservice. And if you are going to use DDD, than you'll want to include a bounded context name in your package path.

In my own source code I usually separate (at the top level) modules like config (to load and parse, well, config), functional for the functional core, domain model, operational for managing concurrency, Akka actors structure, monitoring and so on, and adapters, where all API, DB and MQ code lives. And, at last, module app, where all is launched and interfaces are bound to implementations. Also, you usually have some utils or commons for lower level boilerplate, algorithms and so on.

In some architecture schools there is explicit separation between modules and components. While the former are parts of the source code structure, the latter are runtime units, which consume resources and live in their specific way.

In your case microservices correspond to such components. These components can be run in the same JVM - and you get a monolith. Or they can be run in a separate JVM on a (maybe) separate host. Then you call them microservices.

So, you need to:

  • make each component's source code autonomous so that it could be launched in a separate runtime space (like classloader, thread, threadpool, actor system subtree). Hence, you need some launcher to bring it to life. Then you'll be able to call it from your public static void main(...).
  • introduce some modules in your code that would hold semantics of an individual component each. So that you could understand a component's scope from the code.
  • abstract communication between components, so that you could use adapters (source code modules) to talk over a network, or to use intra-JVM mechanisms like procedure call or Akka's message passing.

I should notice that on the lower levels you can use common source code modules in your components, so they can have some intersections in code. But on the higher level source code would be distinctive, so you can split it into modules according to components.

You can use Akka and run each of your components in a supervision subtree, where the subtree's supervisor is your component's main actor. Then that main actor definition would be your component's main module. If you need to let components communicate, you should pass corresponding ActorRefs to adapters as a config param.

You tell about centralising communication points, but in my opinion, if you stick to microservices paradigm and high level of autonomy for your components, then for every API call somebody has to own a contract. Enter different DDD bounded context interaction patterns. If you leave it in some centrally managed module, which every component should use, then that's a case of API governance. As long as you are the only maintainer, that may be convenient. But when different developers (or even teams) take their parts, you'll need to make this decision once again considering new conditions.

When later you take components apart - then you'll pass URL to adapters instead of ActorRefs.

like image 33
iTollu Avatar answered Sep 30 '22 15:09

iTollu