[
https://issues.apache.org/jira/browse/FINERACT-2169?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18054630#comment-18054630
]
Aleksandar Vidakovic commented on FINERACT-2169:
------------------------------------------------
Currently one community member is working on a module (and finished one that
needs to be reviewed). I will jump on this myself very soon and will
implement/refactor the bulk of the proposed 20 modules. The current list is
roughly half (maybe less) of the code that needs/can be refactored, the more we
do the more complex it will get (obviously we are starting with the
non-critical ones), but that means there is/will be plenty to do here (GSoC or
not). If we include this in GSoC then I suggest to use maybe more of the
"non-critical" modules... unless of course the candidates show that they can
handle more complex ones.
> New command processing infrastructure
> -------------------------------------
>
> Key: FINERACT-2169
> URL: https://issues.apache.org/jira/browse/FINERACT-2169
> Project: Apache Fineract
> Issue Type: New Feature
> Reporter: Aleksandar Vidakovic
> Assignee: Aleksandar Vidakovic
> Priority: Critical
> Labels: FIPS, gsoc2026, mentor
> Fix For: 1.11.0
>
>
> h2. Background and Motivation
> Fineract accumulated some technical debt over the years. One area that is
> implicated is type-safety of internal and external facing APIs, the most
> prominent of which is Fineract's REST API. In general the package layout of
> the project reflects a more or less classic layered architecture (REST API,
> data transfer/value objects, business logic services, storage/repositories).
> The project predates some of the more modern frameworks and best practices
> that are available today and on occasions the data structures that are
> exchanged offer some challenges (e.g. generic types). Fineract's code base
> reflects that, especially where JSON de-/serialization is involved. Nowadays,
> this task would be simply delegated to the Jackson framework, but when
> Fineract (Mifos) started the decision was made to use Google's GSON library
> and create handcrafted helper classes to deal with JSON parsing. While this
> provided a lot of flexibility this approach had some downsides:
> * the lowest common denominator is the string type (aka JSON blob); this is
> where we lose the type information
> * the strings are transformed into JSONObjects; a little bit better than raw
> strings, but barely more than a hash map
> * a ton of "magic" strings are needed to get/set values
> * this approach makes refactoring unnecessarily more difficult
> * to be able to serve an OpenAPI descriptor (as JSON and YAML) we had to
> re-introduce the type information at the REST API level with dummy classes
> that contain only the specified attributes; those classes are only used with
> the Swagger annotations and no were else
> * some developers skipped the layered architecture and found it too tedious
> to maintain DTOs and JSON helper classes, and as a result just passed
> JSONObjects right to the business logic layer
> * now the business logic is unnecessarily aware of how Fineract communicates
> to the outside world and makes replacing/enhancing the communication protocol
> (e.g. with GRPC) pretty much impossible
> The list doesn't end here, but in the end things boil down to two main points:
> * poor developer experience: boilerplate code and missing type safety cost
> more time
> * bugs: the more code the more likely errors get introduced, especially when
> type safety is missing and we have to rely on runtime errors (vs. compile
> time).
> There has been already some preparatory work done concerning type safety, but
> until now we avoided dealing with the real source of this issue. Fineract's
> architectures devises read from write requests ("CQRS",
> [https://martinfowler.com/bliki/CQRS.html]) for improved scalability.
> The read requests are not that problematic, but all write requests pass
> through a component/service that is called
> "SynchronousCommandProcessingService. As the name suggests the execution of
> business logic is synchronous (mostly) due to this part of the architecture.
> This is not necessarily a problem (not immediately at least), but it's
> nevertheless a central bottleneck in the system. Even more important: this
> service is responsible to route incoming commands to their respective handler
> classes which in turn execute functions on one or more business logic
> services. The payload of these commands are obviously not always the same...
> which is the main reason why we decided to use the lowest common denominator
> to be able to handle these various types and rendered all payloads as
> strings. This compromise bubbles now up in the REST API and the business
> logic layers (and actually everything in between).
> Over the years we've also added additional features (e.g. idempotency
> guarantees for incoming write requests) that make it now very hard to reason
> about the execution flow. Testing the performance impact of such additions to
> the critical execution path even can't be properly measured. Note: the
> current implementation of idempotency relies on database lookups (quite
> often, for each incoming request) and none of those queries are cached. If we
> wanted to store already processed requests (IDs) in a faster system (let's
> Redis) then this can't be done without major refactoring.
> In conclusion, if we really want to fix those issues that are not only
> cosmetic and affect the performance and the developer experience equally then
> we urgently need to fix the way how we process write requests aka commands.
> h2. Target Personas
> * developers
> * integrators
> * end users
> * BaaS
> h2. Goals
> * new command processing will run independently next to the legacy mechanics
> * self contained
> * fully tested
> * ensure that the REST API is 100% backward compatible
> * try to contain the migration and make it as easy as possible for the
> community to integrate those changes
> * introduce types where needed and migrate the (old) JAX-RS REST resource
> classes to Spring Web MVC (better performance and better testability)
> * introduce DTOs if not already available and make sure if they exist that
> they are not outdated
> * assemble one DTO as command payload from all incoming REST API parameters
> (headers, query/path paramters, request bodies)
> * annotate attributes in the DTOs with Jakarta Validation annotations to
> enforce constraints on their values
> * wired REST API to the new command processing, one service at a time/pull
> request
> * take a non-critical service (like document management) and migrate it to
> the new command processing mechanics from top (REST API) to bottom (business
> logic service)
> * refactor command handlers to new internal API
> * make sure that the business service logic classes/functions take only one
> DTO request input parameter (aka don't let a function have 12 input
> parameters of type string...)
> * when all integration tests run successfully then remove all legacy
> boilerplate code that is not used anymore
> * make an ordered list of modules/features (easiest, lowest hanging fruit
> first)
> * maintain at least the same performance as the current implementation
> * optional: improve performance if it can be done in a reasonable time frame
> * optional: improve resilience if it can be done in a reasonable time frame
> h2. Non-Goals
> * current command processing will stay untouched, will run independently of
> new infrastructure
> * don't try cleaning up the storage layer; that's a separate effort for
> later (type safe queries, query peformance, clean entity classes)
> * maker-checker is tightly coupled in the current command processing
> implementation upstream; this is a separate concern for a separate proposal
> (domains: security, workflow)
> * doesn't need to be optimized for speed immediately
> * no changes in the integration tests
> h2. Proposed API Changes
> h3. Command Wrapper
> Class contains some generic attributes like:
> * username
> * tenant ID
> * timestamp
> The actual payload (aka command input parameters) are defined as a generic
> parameter "payload". It is expected that the modules implement classes that
> introduce the payload types and inherit from the abstract command class.
> h3. Command Processing Service
> Three performance levels are configurable via application.properties
> * synchronously (required): this is pretty much as we do right now (use
> virtual threads optionally)
> * asynchronously (optional): with executor service and completable futures
> (use virtual threads optionally)
> * non-blocking (optional): high performance LMAX Disruptor non-blocking
> implementation
> These different performance level implementations need to be absolute drop-in
> replacements (for each other). It is expected that more performant
> implementations need more testing due to increased complexity and possible
> unforeseen side effects (thread local variables, transactions). In case any
> problems show up we can always roll back to the required default
> implementation (synchronous).
> NOTE: we should consider providing a command processing implementation based
> on Apache Camel once this concept is approved and we migrated already a
> couple of services. They are specialized for exactly this kind of use cases
> and have more dedicated people working on it's implementation. Could give
> more flexibility without us needing to maintain code.
> h3. Middlewares
> TBD
> h3. Command Handlers
> TBD
> h3. References to users (aka AppUser)
> Keep things lightweight and only reference users by their user names.f
> h2. Risks
> TBD
> * feature creep
> h2. ETA
> A first prototype of the a new command processing component is ready for
> evaluation. There is also an initial smoke test (JMH) available.
> You can try it out with the following instructions (it's still in a private
> repository, but will be published soon as an official PR):
>
> {{git clone [email protected]:vidakovic/fineract.git}}
> {{cd fineract}}
> {{git checkout feature/FINERACT-2169}}
> {{./gradlew :fineract-command:build}}
> {{./gradlew :fineract-command:jmh}}
> h2. Diagrams
> TBD
> h2. Related Jira Tickets
> * https://issues.apache.org/jira/browse/FINERACT-2021
> * https://issues.apache.org/jira/browse/FINERACT-1744
> * https://issues.apache.org/jira/browse/FINERACT-1909
> * https://issues.apache.org/jira/browse/FINERACT-2452
--
This message was sent by Atlassian Jira
(v8.20.10#820010)