How to implement long running business processes
The following is a proposed approach for implementing long running business processes.
This approach isn't conventional - it shuns all application/service level IPC including event messaging
instead leaving all IPC up to the DBMS, where it's about data replication, not messaging.
It requires some capabilities of the DBMS and programming language(s) which are often
unavailable in current products on the market.
- Partition the business into parts (called services) which are somewhat independent.
Make sure these represent vertical slices - i.e. slices through the business domain.
For example Sales, Inventory, and Shipping services.
Note that horizontal slicing is an antipattern.
The vertical slices should separate concerns, and be:
- autonomous
- loosely coupled
- highly cohesive
- Assign software teams to vertical slices
- Use multidisciplinary software teams that encompass:
- requirements / design / implementation / testing
- documentation
- database schema / logic or function / UI
- Use a multi-master replicated embedded distributed DBMS to provide high performance, fault tolerance
and availability (such as CEDA).
Whereever possible applications/services only access data in their local database. This should be
automatically cached in memory by the DBMS.
- Use the relational model to record the current state of the business, typically using predicates
about business events that occur in the world, such as the receival of an order by the Sales
service.
- Don't impose constraints on base representations, instead impose constraints on read-only views
- Don't use Object Relational Mapping (ORM) to map tuples
in relations into object instances. ORM is an antipattern.
Instead use a language where typed relations and tuples are first class citizens.
- Only use a layered design within a process. For example, an application separates
data/function/UI within its process.
This is analogous to a three-tier design, but without deploying the layers to separate processes
or physical machines and using IPC between them.
- Wherever possible avoid IPC in application code.
This includes messaging between processes.
Don't use message brokers like RabbitMQ, NServiceBus or Kafka.
Message brokers represent an antipattern.
Only use in-process messaging (e.g. GUI event handlers or I/O completion events).
- Use views (which are usually read-only derived relvars maintained with incremental computing) for
logical independence to control what information
managed by one service is visible to others.
Note that logical independence provides loose coupling, information hiding and immunisation from
database schema changes.
- Use database triggers (typically defined on read-only derived relvars) to
process events as required.
- Be careful with defining appropriate atomic transactions on a local database.
A transaction should always "balance" in a manner which is analogous to
double entry bookkeeping [].
See
bank transfers without distributed transactions and
Making an on-line purchase.
For example removal of stock from an inventory account balances a commitment to ship that stock
to a customer.
The latter might trigger the shipping to be done by the Shipping service via a database trigger on a
derived relvar which is replicated on a remote machine.