Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
What software architecture are available for developing an application with multiple business domains that only share some common aspects?
NOTE
The meaning of the word "program" below is(PROGRAM) A federal or state initiative that offers grants for non-profits so that they can provide service at low cost or free of charge.
and not a software application or online service. When I mean software, I use "application".
(Thank you @matthewsnyder for pointing this out!)
For example, a non-profit provides services for people with disabilities that has multiple departments where each department can offer multiple "programs". A program is funded by a federal or state grant, and only accepts clients of a certain demographics, such as 55 and older. Each program has its own budget, reporting requirements, scheduling rules, etc.
Usually, the only things these federal/state programs have in common are:
-
the employees providing the services (e.g., one instructor can help out different federal/state programs; a manager can overview multiple departments / federal/state programs; a department can run multiple federal/stateprograms; etc.)
-
the clients receiving the services
These are the architectural options I can think of:
1. Monolith
One application operating on one huge database schema that have all the info from all federal/state programs. Perhaps table names reflect which federal/state program they belong to.
Pros:
- Easy.
Cons:
- I haven't seen one solution yet that wasn't messy (or downright disastrous).
2. One application, but more granular database structure
PostgreSQL has schemas, so one could design a database schema for federal/state programs A, B, and C, and the application would hide this detail. (It is also possible to make cross-database queries with PostgreSQL.)
- How would one share the clients and employees though? (Using an extra schema just for this data?)
Pros:
- Less cognitive load when trying to understand the organization.
- Easier to document.
Cons:
- More involved design process.
- App framework should support schemas (otherwise it can be challenging to do queries).
3. One app and one database per federal/state program
I guess this is what microservices are? There could still be one unified frontend.
Pros:
- ???
Cons:
- Complexity.
- Many points of failure.
- Deep knowledge about managing all the infrastructure.
4 - N. What else?
What haven't I thought of? Did I get some of the ones above totally wrong?
2 answers
If I ignore the example and answer the title generally: You would put the common logic into a third piece of software that becomes the dependency of both domains.
For example, let's say you are writing software for a company that has two domains of business: They sell construction tools and materials for DIYers, and they act as contractors to build stuff for people.
The retail side might need to keep track of things like inventory, orders, shipping, returns, warranties. It would need a credit card processing system. This would be one codebase, but it could be made up of many programs (one repo for mobile app, one repo for public website, one repo for backend...).
The contractor side would need to keep track of jobs, quotes, invoices (people probably don't just swipe a credit card to pay for a million dollar house build), loans. For simplicity, let's say the contractors just buy supplies independently - you have to keep track of business expenses. This would be another codebase, similar to the previous one, but maybe it includes things like software for handling building plans, permits, building codes.
The common stuff is that all these employees have salary data, benefits, taxes, vacations etc. Let's say they operate out of the same store: Keeping track of stores, their expenses, who works where would be common. This would be a common codebase.
For example when someone buys a tool online, the retail app will have to call the common app to check what stores are closest, so it can show the "in stock at a store near you!" message. When people request a construction quote, the contractor side would also query the same thing from the same place.
In simple cases the common logic can be a library, which the other codebases import. If there's a lot of data, it might have to be a database or an API or microservice that they connect to and query. This means you will always have at least two interfaces: Retail-common, and contractor-common. But luckily no retail-contractor interface is needed, those two don't need to know about each other.
In the example you give with the non-profit, I don't know if a separate codebase is needed. It sounds like the problem is actually just how to structure the database tables.
You would just have a set of tables for things in domain 1 only, a set of tables for domain 2 only, and a set of common tables (employees, clients). The per-domain tables would connect to common tables with foreign keys.
So for example the common clients table would have the client ID, name, phone, email of the client. The table for clients going through income adjusted aid would have the client ID, income, other stuff. You would join the income adjusted table with the common client table to get the full data, and that would also remove clients who are not going through the income adjusted. Maybe veterans would have a separate table with client ID, branch of military, date of service, military ID, etc. That would also join to the common clients as needed.
The schemas are not important here. Schemas are like folders for organizing your tables, when you have a lot of tables. They can make it easier to find tables (if you know the schema, you have to look through a smaller list) and bulk-grants (you can give people permission to see all tables in a schema), but everything you can do with schemas you can do without just as easily.
You can see how this is analogous to the general software solution I gave above. The difference is that databases are a lot simpler, and you might not need the overhead of "multiple codebases". It can all live in the same piece of software, you just separate common data using tables.
Another way to describe this would be to use normalized tables.
0 comment threads
Important note: this is primarily based on personal fairly long experience with monolithic architectures and limited microservice-based architecture combined with others' experience with microservice-based architecture
Based on your rather vague requirements and the data volume indicated in the comments, I suggest:
Modular Monolithic Architecture
This can be a good way to start a project even if it will eventually need some microservices (for scaling reasons mostly). This article provides a very good introduction about this architecture, so I will only add some highlights here:
- modules should have clear boundaries. This avoids what you have noticed in legacy systems (messy/disastrous) and can be achieved by using events or a CQRS-like architecture, thus avoiding a direct coupling between modules.
- easier deployment
- simpler infrastructure
- less network overhead (mostly happens in memory)
- easier to reach data consistency, since a transaction does not span to multiple services
- allows for a module to be migrated should it have special scalability needs, require development by another team or it requires to be rewritten in another framework
This article goes deeper into the topic.
For the particular architecture at the module level, I recommend using a clean architecture, as shown in this article.
Upfront separation
If you know that one module of the application has special requirements, it makes to define it as a special service. For example, it is very likely that the employee module should have much more strict security constraints than another module due to the nature of its data (e.g. encryption at rest, encryption in transit).
It would make sense to define it as a separate service with its own small database.
Sacrifice some DRY in order to favor decoupling
This means that you should avoid reusing business logic between the project types unless it is something very generic like an economical indicator that has a universal formula or similar. This ensures that each project type can evolve independently and reduces the likelihood of getting the "mess" found in the legacy projects.
This article explains in very simple terms how sacrificing DRY within reason can lead to a better result.
Broadly speaking this approach also increases stability:
- with business logic sharing: a change request makes you wonder how other project types are affected
- with less/no business logic sharing: a change request affects only the changed project type and you know for certain that other types are not affected. The worst case is that the other types have an old, but stable business logic, as opposed to a possibly broken business logic.
3 comment threads