Still trying to find some nice way how to decouple apps within my umbrella based application. Lets assume I have umbrella base CRM/ERP like app. I have apps like:
customers
projects
helpdesk
…
Now what when project app requires the list of customers for selection box. Common approach to decouple such apps is to expose their api as microservices. But its rather too big for app that contains all the code. But I still want to make straight borders among apps internaly and don’t want apps to reference each other direcly (e.g. by calling Crm.Customers.Repo.list() from project app). I’m thinking about the internal microservice architecture, where each umbrella app would exposes its API via GenServer like process. So when project requires list of customers then project code will not call customers module directly (not calling Crm.Customers.Repo.list()) but rather ask GenServer process for list of users. GenServer message like communication seems to make nice decoupling for inter app comunication.
Using this approach each individual umbrella app would run GenServer api process that would handle inter app communication. Is this right idea or how do you suppose to handle such code decoupling? Do you see any bottlenecks for such approach ?
e.g. having genserver: Customers.API, Projects.API - now I can send message to Customers.API process (via Registry???) with payload {:customers-list} to get list of contacts. The same way I can call {:company-save, %{ data to be saved }} to save record etc.
I like the idea, but I would be very cautious about the granularity of this services. Sometimes we tend to overengineer when it comes to microservices. Instead of doing the microservices separated by resources themselves, I would separate them by bounded contexts (thank you @peerreynders for giving me the name of the concept).
Is the data for these applications actually stored in distinct databases? If it isn’t, you may already be in trouble from the autonomy perspective. If it is all stored in the same database it may make sense to create a separate set of queries for each application - if you have a look at diagram in the Bounded Context article you will notice that both the Sales and Support context have their own, separate Customer representation tailored to the specific needs of that context rather some singular canonical representation - because sharing of all-in-one representations can lead to coupling to irrelevant details.
Even it you decide to go the GenServer route you may want to think about Consumer Driven Contracts - i.e. have the GenServer(s) under the control of the Customer application but deploy a separate GenServer for each distinct Customer representation.
Another tactic is Command Query Responsibility Separation. I.e. if modifications to customers is permitted from outside of the Customer application the simplest implementation could offer separate GenServers for query messages and for update messages - simply to get the benefit of keeping these concerns separate.
Now I’m not saying that any of the above applies to your particular case, likely most if not all would be considered over-engineering. But it’s probably worth your time to explore these options and document why any particular option was dismissed (in many cases it is just as important to know what was considered in the past and why particular avenues were not pursued).
Loose coupling reduce complexity to me there is a fight to do here…
[quote=“dmarko484, post:1, topic:3636”]
But I still want to make straight borders among apps internaly and don’t want apps to reference each other direcly (e.g. by calling Crm.Customers.Repo.list() from project app)
[/quote]
Why ? Where is the problem that make you want to do things like that?
Why not have a client lib and a server lib? There are use case when you may want to not call code directly but… you lose all the advantage of functions and public API idea. The whole idea of a public API is to exactly hide these gateways.
Microservices should be decoupled in time. This means that your project should not be dependent on your CRM system to be up.
this means that you can have your Project service listen for Change Events published by the CRM system and save the relevant information in its own database
that way even if you manage to completely destroy your CRM system you will still have a fully functional Project system