This is an automated email from the ASF dual-hosted git repository.

aleks pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/fineract-site.git


The following commit(s) were added to refs/heads/asf-site by this push:
     new c82ad36  chore: Publish current docs
c82ad36 is described below

commit c82ad36339e92a800c3061f04372ae550b641bbb
Author: Aleksandar Vidakovic <[email protected]>
AuthorDate: Fri Sep 16 00:57:16 2022 +0200

    chore: Publish current docs
---
 docs/current/index.html | 1075 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 1012 insertions(+), 63 deletions(-)

diff --git a/docs/current/index.html b/docs/current/index.html
index 7fcf988..acc4d37 100644
--- a/docs/current/index.html
+++ b/docs/current/index.html
@@ -530,7 +530,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
 <div id="header">
 <h1>Fineract Platform Documentation</h1>
 <div class="details">
-<span id="revnumber">version 0.0.0-d003326e</span>
+<span id="revnumber">version 0.0.0-a313bced</span>
 </div>
 <div id="toc" class="toc2">
 <div id="toctitle">Table of Contents</div>
@@ -573,6 +573,7 @@ table.CodeRay td.code{padding:0 0 0 .75em}
 <li><a href="#_technology">Technology</a></li>
 <li><a href="#_modules">Modules</a></li>
 <li><a 
href="#_introducing_business_date_into_fineract_community_version">Introducing 
Business Date into Fineract - Community version</a></li>
+<li><a href="#_reliable_event_framework">Reliable event framework</a></li>
 </ul>
 </li>
 <li><a href="#_fineract_development_environment">Fineract Development 
Environment</a>
@@ -675,7 +676,7 @@ Website: <a href="https://fineract.apache.org"; 
class="bare">fineract.apache.org<
 Email: <a 
href="mailto:[email protected]";>[email protected]</a></p>
 </div>
 <div class="paragraph">
-<p><strong>Version</strong>: 0.0.0-d003326e</p>
+<p><strong>Version</strong>: 0.0.0-a313bced</p>
 </div>
 <div class="paragraph">
 <p><strong>Date</strong>: 2022-09-09</p>
@@ -1702,7 +1703,7 @@ The implementation of the platform code to process 
commands through handlers whi
 
         <span class="directive">final</span> <span 
class="predefined-type">String</span> json = wrapper.getJson();
         CommandProcessingResult result = <span 
class="predefined-constant">null</span>;
-        JsonCommand command = <span class="predefined-constant">null</span>;
+        JsonCommand command;
         <span class="type">int</span> numberOfRetries = <span 
class="integer">0</span>;
         <span class="type">int</span> maxNumberOfRetries = 
ThreadLocalContextUtil.getTenant().getConnection().getMaxRetriesOnDeadlock();
         <span class="type">int</span> maxIntervalBetweenRetries = 
ThreadLocalContextUtil.getTenant().getConnection().getMaxIntervalBetweenRetries();
@@ -1713,7 +1714,7 @@ The implementation of the platform code to process 
commands through handlers whi
                 wrapper.getOrganisationCreditBureauId());
         <span class="keyword">while</span> (numberOfRetries &lt;= 
maxNumberOfRetries) {
             <span class="keyword">try</span> {
-                result = <span 
class="local-variable">this</span>.processAndLogCommandService.processAndLogCommand(wrapper,
 command, isApprovedByChecker);
+                result = <span 
class="local-variable">this</span>.processAndLogCommandService.executeCommand(wrapper,
 command, isApprovedByChecker);
                 numberOfRetries = maxNumberOfRetries + <span 
class="integer">1</span>;
             } <span class="keyword">catch</span> (CannotAcquireLockException | 
ObjectOptimisticLockingFailureException exception) {
                 log.info(<span class="string"><span 
class="delimiter">&quot;</span><span class="content">The following command {} 
has been retried  {} time(s)</span><span 
class="delimiter">&quot;</span></span>, command.json(), numberOfRetries);
@@ -1757,11 +1758,9 @@ The implementation of the platform code to process 
commands through handlers whi
 <div class="listingblock">
 <div class="title">Check user has permission for this action. if ok, a) parse 
the json request body, b) create a JsonCommand object to wrap the command 
details, c) use CommandProcessingService to handle command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="java">        <span 
class="directive">final</span> AppUser maker = <span 
class="local-variable">this</span>.context.authenticatedUser(wrapper);
-
-        CommandSource commandSourceResult;
+<pre class="CodeRay highlight"><code data-lang="java">        CommandSource 
commandSourceResult;
         <span class="keyword">if</span> (command.commandId() != <span 
class="predefined-constant">null</span>) {
-            commandSourceResult = <span 
class="local-variable">this</span>.commandSourceRepository.findById(command.commandId())
+            commandSourceResult = 
commandSourceRepository.findById(command.commandId())
                     .orElseThrow(() -&gt; <span class="keyword">new</span> 
CommandNotFoundException(command.commandId()));
             commandSourceResult.markAsChecked(maker);
         } <span class="keyword">else</span> {
@@ -1774,7 +1773,7 @@ The implementation of the platform code to process 
commands through handlers whi
         <span class="predefined-type">String</span> changesOnlyJson;
         <span class="type">boolean</span> rollBack = (rollbackTransaction || 
result.isRollbackTransaction()) &amp;&amp; !isApprovedByChecker;
         <span class="keyword">if</span> (result.hasChanges() &amp;&amp; 
!rollBack) {
-            changesOnlyJson = <span 
class="local-variable">this</span>.toApiJsonSerializer.serializeResult(result.getChanges());
+            changesOnlyJson = 
toApiJsonSerializer.serializeResult(result.getChanges());
             commandSourceResult.updateJsonTo(changesOnlyJson);
         }
 
@@ -1783,7 +1782,7 @@ The implementation of the platform code to process 
commands through handlers whi
         }
 
         <span class="keyword">if</span> (commandSourceResult.hasJson()) {
-            <span 
class="local-variable">this</span>.commandSourceRepository.save(commandSourceResult);
+            commandSourceRepository.save(commandSourceResult);
         }
 
         <span class="keyword">if</span> ((rollbackTransaction || 
result.isRollbackTransaction()) &amp;&amp; !isApprovedByChecker) {
@@ -1801,7 +1800,7 @@ The implementation of the platform code to process 
commands through handlers whi
         }
         result.setRollbackTransaction(<span 
class="predefined-constant">null</span>);
 
-        publishEvent(wrapper.entityName(), wrapper.actionName(), command, 
result);
+        publishHookEvent(wrapper.entityName(), wrapper.actionName(), command, 
result);
 
         <span class="keyword">return</span> result;
     }
@@ -1811,7 +1810,7 @@ The implementation of the platform code to process 
commands through handlers whi
     <span class="directive">public</span> CommandProcessingResult 
logCommand(CommandSource commandSourceResult) {
 
         commandSourceResult.markAsAwaitingApproval();
-        commandSourceResult = <span 
class="local-variable">this</span>.commandSourceRepository.saveAndFlush(commandSourceResult);
+        commandSourceResult = 
commandSourceRepository.saveAndFlush(commandSourceResult);
 
         <span class="keyword">return</span> <span class="keyword">new</span> 
CommandProcessingResultBuilder().withCommandId(commandSourceResult.getId())
                 .withEntityId(commandSourceResult.getResourceId()).build();
@@ -1822,8 +1821,9 @@ The implementation of the platform code to process 
commands through handlers whi
 
         <span class="keyword">if</span> (wrapper.isDatatableResource()) {
             <span class="keyword">if</span> (wrapper.isCreateDatatable()) {
-                handler = <span 
class="local-variable">this</span>.applicationContext.getBean(<span 
class="string"><span class="delimiter">&quot;</span><span 
class="content">createDatatableCommandHandler</span><span 
class="delimiter">&quot;</span></span>, NewCommandSourceHandler.class);
-            } <span class="keyword">else</span> <span 
class="keyword">if</span> (wrapper.isDeleteDatatable()) {</code></pre>
+                handler = applicationContext.getBean(<span 
class="string"><span class="delimiter">&quot;</span><span 
class="content">createDatatableCommandHandler</span><span 
class="delimiter">&quot;</span></span>, NewCommandSourceHandler.class);
+            } <span class="keyword">else</span> <span 
class="keyword">if</span> (wrapper.isDeleteDatatable()) {
+                handler = applicationContext.getBean(<span 
class="string"><span class="delimiter">&quot;</span><span 
class="content">deleteDatatableCommandHandler</span><span 
class="delimiter">&quot;</span></span>, 
NewCommandSourceHandler.class);</code></pre>
 </div>
 </div>
 <div class="admonitionblock note">
@@ -3530,6 +3530,948 @@ Feature: Example Modules
 </div>
 </div>
 </div>
+<div class="sect2">
+<h3 id="_reliable_event_framework">Reliable event framework</h3>
+<div class="paragraph">
+<p>Fineract is capable of generating and raising events for external consumers 
in a reliable way. This section is going to describe all the details on that 
front with examples.</p>
+</div>
+<div class="sect3">
+<h4 id="_framework_capabilities">Framework capabilities</h4>
+<div class="sect4">
+<h5 id="_acid_transactional_guarantee">ACID (transactional) guarantee</h5>
+<div class="paragraph">
+<p>The event framework must support ACID guarantees on the business operation 
level.</p>
+</div>
+<div class="paragraph">
+<p>Let’s see a simple use-case:</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>A client applies to a loan on the UI</p>
+</li>
+<li>
+<p>The loan is created on the server</p>
+</li>
+<li>
+<p>A loan creation event is raised</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>What happens if step 3 fails? Shall it fail the original loan creation 
process?</p>
+</div>
+<div class="paragraph">
+<p>What happens if step 2 fails but step 3 still gets executed? We’re raising 
an event for a loan that hasn’t been created in reality.</p>
+</div>
+<div class="paragraph">
+<p>Therefore, raising an event is tied to the original business transaction to 
ensure the data that’s getting written into the database along with the 
respective events are saved in an all-or-nothing fashion.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_messaging_integration">Messaging integration</h5>
+<div class="paragraph">
+<p>The system is able to send the raised events to downstream message 
channels. The current implementation supports the following message 
channels:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>ActiveMQ</p>
+</li>
+</ul>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_ordering_guarantee">Ordering guarantee</h5>
+<div class="paragraph">
+<p>The events that are raised will be sent to the downstream message channels 
in the same order as they were raised.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_delivery_guarantee">Delivery guarantee</h5>
+<div class="paragraph">
+<p>The framework supports the at-least-once delivery guarantee for the raised 
events.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_reliability_and_fault_tolerance">Reliability and fault-tolerance</h5>
+<div class="paragraph">
+<p>In terms of reliability and fault-tolerance, the event framework is able to 
handle the cases when the downstream message channel is not able to accept 
events. As soon as the message channel is back to operational, the events will 
be sent again.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_selective_event_producing">Selective event producing</h5>
+<div class="paragraph">
+<p>Whether or not an event must be sent to downstream message channels for a 
particular Fineract instance is configurable through the UI and API.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_standardized_format">Standardized format</h5>
+<div class="paragraph">
+<p>All the events sent to downstream message channels are conforming a 
standardized format using Avro schemas.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_extendability_and_customizations">Extendability and 
customizations</h5>
+<div class="paragraph">
+<p>The event framework is capable of being easily extended with new events for 
additional business operations or customizing existing events.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_ability_to_send_events_in_bulk">Ability to send events in bulk</h5>
+<div class="paragraph">
+<p>The event framework makes it possible to sort of queue events until they 
are ready to be sent and send them as a single message instead of sending each 
event as a separate, individual one.</p>
+</div>
+<div class="paragraph">
+<p>For example during the COB process, there might be events raised in 
separate business steps which needs to be sent out but they only need to be 
sent out at the end of the COB execution process instead of one-by-one.</p>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_architecture_2">Architecture</h4>
+<div class="sect4">
+<h5 id="_intro">Intro</h5>
+<div class="paragraph">
+<p>On a high-level, the concept looks the following. An event gets raised in a 
business operation. The event data gets saved to the database - to ensure ACID 
guarantees. An asynchronous process takes the saved events from the database 
and puts them onto a message channel.</p>
+</div>
+<div class="paragraph">
+<p>The flow can be seen in the following diagram:</p>
+</div>
+<div class="imageblock">
+<div class="content">
+<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtMAAAGyCAYAAAAmtEhTAAAAAXNSR0IArs4c6QAAIABJREFUeF7svQu4FtV5/n2DCoIKKEQRKirtFzCtoUFjNMBHo0ZIJIlCKuYD/okxkSiEiAEPyEkOxooB5UMNRmIaoBUbjIkYhRgtBeKhSIv2UjAtKh5C0o0ixI2H6P5fz7ifN2svZt6Zeec8c891bWXvmbXWs37Peue955lnrdUOPEiABEiABEiABEiABEiABBoi0K6hUixEAiRAAiRAAiRAAiRAAiQAimkOAhIgARIgARIgARIgARJokADFdIPgWIwESIAESIAESIAESIAEKKY5BkiABEiABEiABEiABEigQQIU0w2CYzESIAESIAESIAESIAESoJjmGCABEiABEiABEiABEiCBBglQTDcIjsVIgARIgARIgARIgARIgGKaY4AESIAESIAE
 [...]
+</div>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_foundational_business_events">Foundational business events</h5>
+<div class="paragraph">
+<p>The whole framework is built upon an existing infrastructure in Fineract; 
the Business Events.</p>
+</div>
+<div class="paragraph">
+<p>As a quick recap, Business Events are Fineract events that can be raised at 
any place in a business operation using the 
<code>BusinessEventNotifierService</code>. Callbacks can be registered when a 
certain type of Business Event is raised and other business operations can be 
done. For example when a Loan gets disbursed, there&#8217;s an interested party 
doing the Loan Arrears Aging recalculation using the Business Event 
communication.</p>
+</div>
+<div class="paragraph">
+<p>The nice thing about the Business Events is that they are tied to the 
original transaction which means if any of the processing on the 
subscriber&#8217;s side fail, the entire original transaction will be rolled 
back. This was one of the requirements for the Reliable event framework.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_event_database_integration">Event database integration</h5>
+<div class="paragraph">
+<p>The database plays a crucial part in the framework since to ensure 
transactionality, - without doing proper transaction synchronization between 
different message channels and the database - the framework is going to save 
all the raised events into the same relational database that Fineract is 
using.</p>
+</div>
+<div class="sect5">
+<h6 id="_database_structure">Database structure</h6>
+<div class="paragraph">
+<p>The database structure looks the following</p>
+</div>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 25%;">
+<col style="width: 25%;">
+<col style="width: 25%;">
+</colgroup>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><strong>Name</strong></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><strong>Type</strong></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><strong>Description</strong></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><strong>Example</strong></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>id</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">number</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Auto 
incremented ID.<br>
+<br>
+Not null.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>1</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>type</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">text</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The event 
type as a string.<br>
+<br>
+Not null.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>LoanApprovedBusinessEvent</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>schema</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">text</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The fully 
qualified name of the schema that was used for the data serialization, as a 
string.<br>
+<br>
+Not null.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>org.apache.fineract.avro.loan.v1.LoanAccountDataV1</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>data</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">BLOB 
(MySQL/MariaDB), BYTEA (PostgreSQL)</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The event 
payload as Avro binary.<br>
+<br>
+Not null.</p></td>
+<td class="tableblock halign-left valign-top"></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>created_at</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">timestamp</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">UTC 
timestamp when the event was raised.<br>
+<br>
+Not null.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>2022-09-06 14:20:10.148627 +00:00</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>status</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">text</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Enum text 
representing the status of the external event.<br>
+<br>
+Not null, indexed.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>TO_BE_SENT</code>, <code>SENT</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>sent_at</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">timestamp</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">UTC 
timestamp when the event was sent.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>2022-09-06 14:30:10.148627 +00:00</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>idempotency_key</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">text</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Randomly 
generated UUID upon inserting a row into the table for idempotency purposes.<br>
+<br>
+Not null.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>68aed085-8235-4722-b27d-b38674c19445</code></p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>business_date</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">date</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">The 
business date to when the event was generated.<br>
+<br>
+Not null, indexed.</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>2022-09-05</code></p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>The above database table contains the unsent events which later on will be 
sent by an asynchronous event processor.</p>
+</div>
+<div class="paragraph">
+<p>Upon successfully sending an event, the corresponding statuses will be 
updated.</p>
+</div>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_avro_schemas">Avro schemas</h5>
+<div class="paragraph">
+<p>For serializing events, Fineract is using Apache Avro. There are 2 reasons 
for that:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>More compact storage since Avro is a binary format</p>
+</li>
+<li>
+<p>The Avro schemas are published with Fineract as a separate JAR so event 
consumers can directly map the events into POJOs</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>There are 3 different levels of Avro schemas used in Fineract for the 
Reliable event framework which are described below.</p>
+</div>
+<div class="sect5">
+<h6 id="_standard_event_schema">Standard event schema</h6>
+<div class="paragraph">
+<p>The standard event schema is for the regular events. These schemas are used 
when saving a raised event into the database and using the Avro schema to 
serialize the event data into a binary format.</p>
+</div>
+<div class="paragraph">
+<p>For example the OfficeDataV1 Avro schema looks the following:</p>
+</div>
+<details>
+<summary class="title"><code>OfficeDataV1.avsc</code></summary>
+<div class="content">
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="avroschema">{
+    &quot;name&quot;: &quot;OfficeDataV1&quot;,
+    &quot;namespace&quot;: &quot;org.apache.fineract.avro.office.v1&quot;,
+    &quot;type&quot;: &quot;record&quot;,
+    &quot;fields&quot;: [
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;id&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;long&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;name&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;string&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;nameDecorated&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;string&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;externalId&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;string&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;openingDate&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;string&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;hierarchy&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;string&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;parentId&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;long&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;parentName&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                &quot;string&quot;
+            ]
+        },
+        {
+            &quot;default&quot;: null,
+            &quot;name&quot;: &quot;allowedParents&quot;,
+            &quot;type&quot;: [
+                &quot;null&quot;,
+                {
+                    &quot;type&quot;: &quot;array&quot;,
+                    &quot;items&quot;: 
&quot;org.apache.fineract.avro.office.v1.OfficeDataV1&quot;
+                }
+            ]
+        }
+    ]
+}</code></pre>
+</div>
+</div>
+</div>
+</details>
+</div>
+<div class="sect5">
+<h6 id="_event_message_schema">Event message schema</h6>
+<div class="paragraph">
+<p>The event message schema is just a wrapper around the standard event schema 
with extra metadata for the event consumers.</p>
+</div>
+<div class="paragraph">
+<p>Since Avro is strongly typed, the event content needs to be first 
serialized into a byte sequence and that needs to be wrapped around.</p>
+</div>
+<div class="paragraph">
+<p>This implies that for putting a single event message onto a message queue 
for external consumption, data needs to be serialized 2 times; this is the 
2-level serialization.</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Serializing the event</p>
+</li>
+<li>
+<p>Serializing the already serialized event into an event message using the 
message wrapper</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>The message schema looks the following:</p>
+</div>
+<div class="listingblock">
+<div class="title"><code>MessageV1.avsc</code></div>
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="avroschema">{
+    &quot;name&quot;: &quot;MessageV1&quot;,
+    &quot;namespace&quot;: &quot;org.apache.fineract.avro&quot;,
+    &quot;type&quot;: &quot;record&quot;,
+    &quot;fields&quot;: [
+        {
+            &quot;name&quot;: &quot;id&quot;,
+            &quot;doc&quot;: &quot;The ID of the message to be sent&quot;,
+            &quot;type&quot;: &quot;int&quot;
+        },
+        {
+            &quot;name&quot;: &quot;source&quot;,
+            &quot;doc&quot;: &quot;A unique identifier of the source 
service&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;type&quot;,
+            &quot;doc&quot;: &quot;The type of event the payload refers to. 
For example LoanApprovedBusinessEvent&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;category&quot;,
+            &quot;doc&quot;: &quot;The category of event the payload refers 
to. For example LOAN&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;createdAt&quot;,
+            &quot;doc&quot;: &quot;The UTC time of when the event has been 
raised; in ISO_LOCAL_DATE_TIME format. For example 2011-12-03T10:15:30&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;tenantId&quot;,
+            &quot;doc&quot;: &quot;The tenantId that the event has been sent 
from. For example default&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;idempotencyKey&quot;,
+            &quot;doc&quot;: &quot;The idempotency key for this particular 
event for consumer de-duplication&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;dataschema&quot;,
+            &quot;doc&quot;: &quot;The fully qualified name of the schema of 
the event payload. For example 
org.apache.fineract.avro.loan.v1.LoanAccountDataV1&quot;,
+            &quot;type&quot;: &quot;string&quot;
+        },
+        {
+            &quot;name&quot;: &quot;data&quot;,
+            &quot;doc&quot;: &quot;The payload data serialized into Avro 
bytes&quot;,
+            &quot;type&quot;: &quot;bytes&quot;
+        }
+    ]
+}</code></pre>
+</div>
+</div>
+</div>
+<div class="sect5">
+<h6 id="_bulk_event_schema">Bulk event schema</h6>
+<div class="paragraph">
+<p>The bulk event schema is used when multiple events are supposed to be sent 
together. This schema is used also when serializing the data for the database 
storing but the idea is quite simple. Have an array of other event schemas 
embedded into it.</p>
+</div>
+<div class="paragraph">
+<p>Since Avro is strongly typed, the array within the bulk event schema is an 
array of <code>MessageV1</code> schemas. That way the consumers can decide 
which events they want to deserialize and which don&#8217;t.</p>
+</div>
+<div class="paragraph">
+<p>This elevates the regular 2-level serialization/deserialization concept up 
to a 3-level one:</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Serializing the standard events</p>
+</li>
+<li>
+<p>Serializing the standard events into a bulk event</p>
+</li>
+<li>
+<p>Serializing the bulk event into an event message</p>
+</li>
+</ol>
+</div>
+</div>
+<div class="sect5">
+<h6 id="_versioning">Versioning</h6>
+<div class="paragraph">
+<p>Avro is quite strict with changes to an existing schema and there are a 
number of compatibility modes available.</p>
+</div>
+<div class="paragraph">
+<p>Fineract keeps it simple though. Version numbers - in the package names and 
in the schema names - are increased with each published modification; meaning 
that if the OfficeDataV1 schema needs a new field and the 
<code>OfficeDataV1</code> schema has been published officially with Fineract, a 
new <code>OfficeDataV2</code> has to be created with the new field instead of 
modifying the existing schema.</p>
+</div>
+<div class="paragraph">
+<p>This pattern ensures that a certain event is always deserialized with the 
appropriate schema definition, otherwise the deserialization could fail.</p>
+</div>
+</div>
+<div class="sect5">
+<h6 id="_code_generation">Code generation</h6>
+<div class="paragraph">
+<p>The Avro schemas are described as JSON documents. That&#8217;s hardly 
usable directly with Java hence Fineract generates Java POJOs from the Avro 
schemas. The good thing about these POJOs is the fact that they can be 
serialized/deserialized in themselves without any magic since they have a 
<code>toByteBuffer</code> and <code>fromByteBuffer</code> method.</p>
+</div>
+<div class="paragraph">
+<p>From POJO to ByteBuffer:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="java">LoanAccountDataV1 
avroDto = ...
+ByteBuffer buffer = avroDto.toByteBuffer();</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>From ByteBuffer to POJO:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="java"><span 
class="predefined-type">ByteBuffer</span> buffer = ...
+LoanAccountDataV1 avroDto = 
LoanAccountDataV1.fromByteBuffer(buffer);</code></pre>
+</div>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+The ByteBuffer is a stateful container and needs to be handled carefully. 
Therefore Fineract has a built-in ByteBuffer to byte array converter; 
<code>ByteBufferConverter</code>.
+</td>
+</tr>
+</table>
+</div>
+</div>
+<div class="sect5">
+<h6 id="_downstream_event_consumption">Downstream event consumption</h6>
+<div class="paragraph">
+<p>When consuming events on the other side of the message channel, it&#8217;s 
critical to know which events the system is interested in. With the multi-level 
serialization, it&#8217;s possible to deserialize only parts of the message and 
decide based on that whether it makes sense for a particular system to 
deserialize the event payload more.</p>
+</div>
+<div class="paragraph">
+<p>Whether events are important can be decided based on:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>the <code>type</code> attribute in the message</p>
+</li>
+<li>
+<p>the <code>category</code> attribute in the message</p>
+</li>
+<li>
+<p>the <code>dataschema</code> attribute in the message</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>These are the main attributes in the message wrapper one can use to decide 
whether an event message is useful.</p>
+</div>
+<div class="paragraph">
+<p>If the event needs to be deserialized, the next step is to find the 
corresponding schema definition. That&#8217;s going to be sent in the 
<code>dataschema</code> attribute within the message wrapper. Since the 
attribute contains the fully-qualified name of the respective schema, it can be 
easily resolved to a Class object. Based on that class, the payload data can be 
easily deserialized using the <code>fromByteBuffer</code> method on every 
generated schema POJO.</p>
+</div>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_message_ordering">Message ordering</h5>
+<div class="paragraph">
+<p>One of the requirements for the framework is to provide ordering 
guarantees. All the events have to conform a happens-before relation.</p>
+</div>
+<div class="paragraph">
+<p>For the downstream consumers, this can be verified by the <code>id</code> 
attribute within the messages. Since it&#8217;s going to be a 
strictly-monotonic numeric sequence, it can be used for ordering purposes.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_event_categorization">Event categorization</h5>
+<div class="paragraph">
+<p>For easier consumption, the terminology event category is introduced. This 
is nothing else but the bounded context an event is related to.</p>
+</div>
+<div class="paragraph">
+<p>For example the LoanApprovedBusinessEvent and the 
LoanWaiveInterestBusinessEvent are both related to the Loan bounded 
contexts.</p>
+</div>
+<div class="paragraph">
+<p>The category in which an event resides in is included in the message under 
the <code>category</code> attribute.</p>
+</div>
+<div class="paragraph">
+<p>The existing event categories can be found under the <a 
href="#event-categories">Event categories</a> section.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_asynchronous_event_processor">Asynchronous event processor</h5>
+<div class="paragraph">
+<p>The events stored in the database will be picked up and sent by a regularly 
executed job.</p>
+</div>
+<div class="paragraph">
+<p>This job is a Fineract job, scheduled to run for every minute and will pick 
a number of events in order. Those events will be put onto the downstream 
message channel in the same order as they were raised.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_purging_events">Purging events</h5>
+<div class="paragraph">
+<p>The events database table is going to grow continuously. That&#8217;s why 
Fineract has a purging functionality in place that&#8217;s gonna delete old and 
already sent events.</p>
+</div>
+<div class="paragraph">
+<p>It&#8217;s implemented as a Fineract job and is disabled by default. 
It&#8217;s called TBD.</p>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_usage">Usage</h4>
+<div class="paragraph">
+<p>Using the event framework is quite simple. First, it has to be enabled 
through properties or environment variable.</p>
+</div>
+<div class="paragraph">
+<p>The respective options are the following:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>the <code>fineract.events.external.enabled</code> property</p>
+</li>
+<li>
+<p>the <code>FINERACT_EXTERNAL_EVENTS_ENABLED</code> environment variable</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>These configurations accept a boolean value; <code>true</code> or 
<code>false</code>.</p>
+</div>
+<div class="paragraph">
+<p>The key component to interact with is the 
<code>BusinessEventNotifierService#notifyPostBusinessEvent</code> method.</p>
+</div>
+<div class="sect4">
+<h5 id="_raising_events">Raising events</h5>
+<div class="paragraph">
+<p>Raising events is really easy. An instance of a BusinessEvent interface is 
needed, that&#8217;s going to be the event. There are plenty of them available 
already in the Fineract codebase.</p>
+</div>
+<div class="paragraph">
+<p>And that&#8217;s pretty much it. Everything else is taken care of in terms 
of event data persisting and later on putting it onto a message channel.</p>
+</div>
+<div class="paragraph">
+<p>An example of event raising:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="java"><span 
class="annotation">@Override</span>
+<span class="directive">public</span> CommandProcessingResult 
createClient(<span class="directive">final</span> JsonCommand command) {
+    ...
+    businessEventNotifierService.notifyPostBusinessEvent(<span 
class="keyword">new</span> ClientCreateBusinessEvent(newClient));
+    ...
+    return ...;
+}</code></pre>
+</div>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+The above code is copied from the 
<code>ClientWritePlatformServiceJpaRepositoryImpl</code> class.
+</td>
+</tr>
+</table>
+</div>
+<div class="sect5">
+<h6 id="_example_event_message_content">Example event message content</h6>
+<div class="paragraph">
+<p>Since the message is serialized into binary format, it&#8217;s hard to 
represent in the documentation therefore here&#8217;s a JSON representation of 
the data, just as an example.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="json">{
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">id</span><span class="delimiter">&quot;</span></span>: <span 
class="integer">121</span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">source</span><span class="delimiter">&quot;</span></span>: 
<span class="string"><span class="delimiter">&quot;</span><span 
class="content">a65d759d-04f9-4ddf-ac52-34fa5d1f5a25</span><span 
class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">type</span><span class="delimiter">&quot;</span></span>: <span 
class="string"><span class="delimiter">&quot;</span><span 
class="content">LoanApprovedBusinessEvent</span><span 
class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">category</span><span class="delimiter">&quot;</span></span>: 
<span class="string"><span class="delimiter">&quot;</span><span 
class="content">Loan</span><span class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">createdAt</span><span class="delimiter">&quot;</span></span>: 
<span class="string"><span class="delimiter">&quot;</span><span 
class="content">2022-09-05T10:15:30</span><span 
class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">tenantId</span><span class="delimiter">&quot;</span></span>: 
<span class="string"><span class="delimiter">&quot;</span><span 
class="content">default</span><span class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">idempotencyKey</span><span 
class="delimiter">&quot;</span></span>: <span class="string"><span 
class="delimiter">&quot;</span><span 
class="content">abda146d-68b5-48ca-b527-16d2b7c5daef</span><span 
class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">dataschema</span><span class="delimiter">&quot;</span></span>: 
<span class="string"><span class="delimiter">&quot;</span><span 
class="content">org.apache.fineract.avro.loan.v1.LoanAccountDataV1</span><span 
class="delimiter">&quot;</span></span>,
+    <span class="key"><span class="delimiter">&quot;</span><span 
class="content">data</span><span class="delimiter">&quot;</span></span>: <span 
class="string"><span class="delimiter">&quot;</span><span 
class="content">...</span><span class="delimiter">&quot;</span></span>
+}</code></pre>
+</div>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+The source attribute refers to an ID that&#8217;s identifying the producer 
service. Fineract will regenerate this ID upon each application startup.
+</td>
+</tr>
+</table>
+</div>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_raising_bulk_events">Raising bulk events</h5>
+<div class="paragraph">
+<p>Raising bulk events is really easy as well. The 2 key methods are:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><code>BusinessEventNotifierService#startExternalEventRecording</code></p>
+</li>
+<li>
+<p><code>BusinessEventNotifierService#stopExternalEventRecording</code></p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>First, you have to start recording your events. This recording will be 
applied for the current thread. And then you can raise as many events as you 
want with the regular 
<code>BusinessEventNotifierService#notifyPostBusinessEvent</code> method, but 
they won&#8217;t get saved to the database immediately. They&#8217;ll get 
"recorded" into an internal buffer.</p>
+</div>
+<div class="paragraph">
+<p>When you stop recording using the method above, all the recorded events 
will be saved as a bulk event to the database; and serialized appropriately.</p>
+</div>
+<div class="paragraph">
+<p>From then on, the bulk event works just like any of the event. It&#8217;ll 
be picked up by the processor to send it to a message channel.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="event-categories">Event categories</h5>
+<div class="paragraph">
+<p>TBD</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_selective_event_producing_2">Selective event producing</h5>
+<div class="paragraph">
+<p>TBD</p>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_customizations">Customizations</h4>
+<div class="paragraph">
+<p>The framework provides a number of customization options:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Creating new events (that&#8217;s already given by the Business Events)</p>
+</li>
+<li>
+<p>Creating new Avro schemas</p>
+</li>
+<li>
+<p>Customizing what data gets serialized for existing events</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>In the upcoming sections, that&#8217;s what going to be discussed.</p>
+</div>
+<div class="sect4">
+<h5 id="_creating_new_events">Creating new events</h5>
+<div class="paragraph">
+<p>Creating new events is super easy. Just create an implementation of the 
<code>BusinessEvent</code> interface and that&#8217;s it.</p>
+</div>
+<div class="paragraph">
+<p>From then on, you can raise those events in the system, although you 
can&#8217;t publish them to an external message channel. If you have the event 
framework enabled, it&#8217;s going to fail with not finding the appropriate 
serializer for your business event.</p>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+There are existing serializers which might be able to handle your new event. 
For example the <code>LoanBusinessEventSerializer</code> is capable of handling 
all <code>LoanBusinessEvent</code> subclasses so there&#8217;s no need to 
create a brand new serializer.
+</td>
+</tr>
+</table>
+</div>
+<div class="paragraph">
+<p>The interface looks the following:</p>
+</div>
+<div class="listingblock">
+<div class="title"><code>BusinessEvent.java</code></div>
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="java"><span 
class="directive">public</span> <span class="type">interface</span> <span 
class="class">BusinessEvent</span>&lt;T&gt; {
+
+    T get();
+
+    <span class="predefined-type">String</span> getType();
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Quite simple. The <code>get</code> method should return the data you want 
to pass within the event instance. The <code>getType</code> method returns the 
name of the business event that&#8217;s gonna be saved as the <code>type</code> 
into the database.</p>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+Creating a new business event only means that it can be used for raising an 
event. To make it compatible with the event framework and to be sent to a 
message channel, some extra work is needed which are described below.
+</td>
+</tr>
+</table>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_creating_new_avro_schemas_and_serializers">Creating new Avro schemas 
and serializers</h5>
+<div class="paragraph">
+<p>First let&#8217;s talk about the event serializers because that&#8217;s 
what&#8217;s needed to make a new event compatible with the framework.</p>
+</div>
+<div class="paragraph">
+<p>The serializer has a special interface, 
<code>BusinessEventSerializer</code>.</p>
+</div>
+<div class="listingblock">
+<div class="title"><code>BusinessEventSerializer.java</code></div>
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="java"><span 
class="directive">public</span> <span class="type">interface</span> <span 
class="class">BusinessEventSerializer</span> {
+
+    &lt;T&gt; <span class="type">boolean</span> 
canSerialize(BusinessEvent&lt;T&gt; event);
+
+    &lt;T&gt; <span class="type">byte</span><span class="type">[]</span> 
serialize(BusinessEvent&lt;T&gt; rawEvent) <span 
class="directive">throws</span> <span class="exception">IOException</span>;
+
+    <span class="predefined-type">Class</span>&lt;? <span 
class="directive">extends</span> GenericContainer&gt; getSupportedSchema();
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>An implementation of this interface shall be registered as a Spring bean, 
and it&#8217;ll be picked up automatically by the framework.</p>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+You can look at the existing serializers for implementation ideas.
+</td>
+</tr>
+</table>
+</div>
+<div class="paragraph">
+<p>New Avro schemas can be easily created. Just create a new Avro schema file 
in the <code>fineract-avro-schemas</code> project under the respective bounded 
context folder, and it will be picked up automatically by the code 
generator.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_bigdecimal_support_in_avro_schemas">BigDecimal support in Avro 
schemas</h5>
+<div class="paragraph">
+<p>Apache Avro by default doesn&#8217;t support complex types like a 
BigDecimal. It has to be implemented using a custom snippet like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="avroschema">{
+    &quot;logicalType&quot;: &quot;decimal&quot;,
+    &quot;precision&quot;: 20,
+    &quot;scale&quot;: 8,
+    &quot;type&quot;: &quot;bytes&quot;
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>It&#8217;s a 20 precision and 8 scale BigDecimal.</p>
+</div>
+<div class="paragraph">
+<p>Obviously it&#8217;s quite challenging to copy-paste this snippet to every 
single BigDecimal field, so there&#8217;s a customization in place for 
Fineract.<br>
+The type <code>bigdecimal</code> is supported natively, and you&#8217;re free 
to use it like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="avroschema">{
+    &quot;default&quot;: null,
+    &quot;name&quot;: &quot;principal&quot;,
+    &quot;type&quot;: [
+        &quot;null&quot;,
+        &quot;bigdecimal&quot;
+    ]
+}</code></pre>
+</div>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+This <code>bigdecimal</code> type will be simple replaced with the BigDecimal 
snippet showed above during the compilation process.
+</td>
+</tr>
+</table>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_custom_data_serialization_for_existing_events">Custom data 
serialization for existing events</h5>
+<div class="paragraph">
+<p>In case there&#8217;s a need some extra bit of information within the event 
message that the default serializers are not providing, you can override this 
behavior by registering a brand-new custom serializer (as shown above).</p>
+</div>
+<div class="paragraph">
+<p>Since there&#8217;s a priority order of serializers, the only thing the 
custom serializer need to do is to be annotated by the <code>@Order</code> 
annotation or to implement the <code>Ordered</code> interface.</p>
+</div>
+<div class="paragraph">
+<p>An example custom serializer with priority looks the following:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="CodeRay highlight"><code data-lang="java"><span 
class="annotation">@Component</span>
+<span class="annotation">@RequiredArgsConstructor</span>
+<span class="annotation">@Order</span>(Ordered.HIGHEST_PRECEDENCE)
+<span class="directive">public</span> <span class="type">class</span> <span 
class="class">CustomLoanBusinessEventSerializer</span> <span 
class="directive">implements</span> BusinessEventSerializer {
+    ...
+
+    <span class="annotation">@Override</span>
+    <span class="directive">public</span> &lt;T&gt; <span 
class="type">boolean</span> canSerialize(BusinessEvent&lt;T&gt; event) {
+        <span class="keyword">return</span> ...;
+    }
+
+    <span class="annotation">@Override</span>
+    <span class="directive">public</span> &lt;T&gt; <span 
class="type">byte</span><span class="type">[]</span> 
serialize(BusinessEvent&lt;T&gt; rawEvent) <span 
class="directive">throws</span> <span class="exception">IOException</span> {
+        ...
+        ByteBuffer buffer = avroDto.toByteBuffer();
+        <span class="keyword">return</span> 
byteBufferConverter.convert(buffer);
+    }
+
+    <span class="annotation">@Override</span>
+    <span class="directive">public</span> <span 
class="predefined-type">Class</span>&lt;? <span 
class="directive">extends</span> GenericContainer&gt; getSupportedSchema() {
+        <span class="keyword">return</span> ...;
+    }
+}</code></pre>
+</div>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+All the default serializers are having <code>Ordered.LOWEST_PRECEDENCE</code>.
+</td>
+</tr>
+</table>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_appendix_a_properties_and_environment_variables">Appendix A: 
Properties and environment variables</h4>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 25%;">
+<col style="width: 25%;">
+<col style="width: 25%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Property name</th>
+<th class="tableblock halign-left valign-top">Environment variable</th>
+<th class="tableblock halign-left valign-top">Default value</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>fineract.events.external.enabled</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>FINERACT_EXTERNAL_EVENTS_ENABLED</code></p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>false</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Whether 
the external event sending is enabled or disabled.</p></td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
 </div>
 </div>
 <div class="sect1">
@@ -5854,7 +6796,7 @@ ${project['fineract.config.name']}
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep1 -Pfineract.release.issue=1234 
-Pfineract.release.date=&quot;Monday, April 25, 2022&quot; 
-Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep1 -Pfineract.release.issue=1234 
-Pfineract.release.date=&quot;Monday, April 25, 2022&quot; 
-Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -5876,7 +6818,7 @@ ${project['fineract.config.name']}
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="text">project = FINERACT and 
fixVersion = 0.0.0-d003326e and status not in ( Resolved, Done, Accepted, 
Closed )</code></pre>
+<pre class="CodeRay highlight"><code data-lang="text">project = FINERACT and 
fixVersion = 0.0.0-a313bced and status not in ( Resolved, Done, Accepted, 
Closed )</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -5884,7 +6826,7 @@ ${project['fineract.config.name']}
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="text">project = FINERACT and 
fixVersion = 0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="text">project = FINERACT and 
fixVersion = 0.0.0-a313bced</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -5896,7 +6838,7 @@ ${project['fineract.config.name']}
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep2 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep2 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 <div class="admonitionblock caution">
@@ -5954,7 +6896,7 @@ This task is not yet automated!
 <p>Create a new release branch with name "$Version"</p>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% git checkout -b 
0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% git checkout -b 
0.0.0-a313bced</code></pre>
 </div>
 </div>
 </li>
@@ -5962,7 +6904,7 @@ This task is not yet automated!
 <p>Push new branch to Apache Fineract repository</p>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% git push origin 
0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% git push origin 
0.0.0-a313bced</code></pre>
 </div>
 </div>
 </li>
@@ -6001,7 +6943,7 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep3 -Pfineract.release.date=&quot;Monday, May 10, 2022&quot; 
-Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep3 -Pfineract.release.date=&quot;Monday, May 10, 2022&quot; 
-Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -6045,10 +6987,10 @@ This task is not yet automated!
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% git checkout 
0.0.0-d003326e
+<pre class="CodeRay highlight"><code data-lang="bash">% git checkout 
0.0.0-a313bced
 % ./gradlew clean integrationTests <i class="conum" 
data-value="1"></i><b>(1)</b>
-% git tag -a 0.0.0-d003326e -m &quot;Fineract 0.0.0-d003326e release&quot;
-% git push origin 0.0.0-d003326e</code></pre>
+% git tag -a 0.0.0-a313bced -m &quot;Fineract 0.0.0-a313bced release&quot;
+% git push origin 0.0.0-a313bced</code></pre>
 </div>
 </div>
 <div class="colist arabic">
@@ -6077,7 +7019,7 @@ It is important to create so called annotated tags (vs. 
lightweight) for release
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep5 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep5 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -6092,12 +7034,12 @@ It is important to create so called annotated tags (vs. 
lightweight) for release
 <div class="listingblock">
 <div class="content">
 <pre class="CodeRay highlight"><code data-lang="bash">% cd 
/fineract-release-preparations <i class="conum" data-value="1"></i><b>(1)</b>
-% tar -xvf apache-fineract-0.0.0-d003326e-src.tar.gz
+% tar -xvf apache-fineract-0.0.0-a313bced-src.tar.gz
 % git clone <a href="https://git-wip-us.apache.org/repos/asf/fineract.git"; 
class="bare">git-wip-us.apache.org/repos/asf/fineract.git</a>
 % cd fineract/
-% git checkout tags/0.0.0-d003326e
+% git checkout tags/0.0.0-a313bced
 % cd ..
-% diff -r fineract apache-fineract-0.0.0-d003326e-src</code></pre>
+% diff -r fineract apache-fineract-0.0.0-a313bced-src</code></pre>
 </div>
 </div>
 <div class="colist arabic">
@@ -6113,7 +7055,7 @@ It is important to create so called annotated tags (vs. 
lightweight) for release
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% cd 
apache-fineract-0.0.0-d003326e-src/fineract-provider <i class="conum" 
data-value="1"></i><b>(1)</b>
+<pre class="CodeRay highlight"><code data-lang="bash">% cd 
apache-fineract-0.0.0-a313bced-src/fineract-provider <i class="conum" 
data-value="1"></i><b>(1)</b>
 % gradlew clean integrationTest <i class="conum" data-value="2"></i><b>(2)</b>
 % gradlew clean build <i class="conum" data-value="3"></i><b>(3)</b>
 % gradlew rat <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
@@ -6159,12 +7101,12 @@ It is important to create so called annotated tags (vs. 
lightweight) for release
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% gpg --armor --output 
apache-fineract-0.0.0-d003326e-src.tar.gz.asc --detach-sig 
apache-fineract-0.0.0-d003326e-src.tar.gz
-% gpg --print-md MD5 apache-fineract-0.0.0-d003326e-src.tar.gz &gt; 
apache-fineract-0.0.0-d003326e-src.tar.gz.md5
-% gpg --print-md SHA512 apache-fineract-0.0.0-d003326e-src.tar.gz &gt; 
apache-fineract-0.0.0-d003326e-src.tar.gz.sha512
-% gpg --armor --output apache-fineract-0.0.0-d003326e--binary.tar.gz.asc 
--detach-sig apache-fineract-0.0.0-d003326e-binary.tar.gz
-% gpg --print-md MD5 apache-fineract-0.0.0-d003326e-binary.tar.gz &gt; 
apache-fineract-0.0.0-d003326e-binary.tar.gz.md5
-% gpg --print-md SHA512 apache-fineract-0.0.0-d003326e-binary.tar.gz &gt; 
apache-fineract-0.0.0-d003326e-binary.tar.gz.sha512</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% gpg --armor --output 
apache-fineract-0.0.0-a313bced-src.tar.gz.asc --detach-sig 
apache-fineract-0.0.0-a313bced-src.tar.gz
+% gpg --print-md MD5 apache-fineract-0.0.0-a313bced-src.tar.gz &gt; 
apache-fineract-0.0.0-a313bced-src.tar.gz.md5
+% gpg --print-md SHA512 apache-fineract-0.0.0-a313bced-src.tar.gz &gt; 
apache-fineract-0.0.0-a313bced-src.tar.gz.sha512
+% gpg --armor --output apache-fineract-0.0.0-a313bced--binary.tar.gz.asc 
--detach-sig apache-fineract-0.0.0-a313bced-binary.tar.gz
+% gpg --print-md MD5 apache-fineract-0.0.0-a313bced-binary.tar.gz &gt; 
apache-fineract-0.0.0-a313bced-binary.tar.gz.md5
+% gpg --print-md SHA512 apache-fineract-0.0.0-a313bced-binary.tar.gz &gt; 
apache-fineract-0.0.0-a313bced-binary.tar.gz.sha512</code></pre>
 </div>
 </div>
 </div>
@@ -6183,33 +7125,33 @@ It is important to create so called annotated tags (vs. 
lightweight) for release
 <div class="sect4">
 <h5 id="_description_8">Description</h5>
 <div class="paragraph">
-<p>Finally create a directory with release name (0.0.0-d003326e in this 
example) in <a href="https://dist.apache.org/repos/dist/dev/fineract"; 
class="bare">dist.apache.org/repos/dist/dev/fineract</a> and add the following 
files in this new directory:</p>
+<p>Finally create a directory with release name (0.0.0-a313bced in this 
example) in <a href="https://dist.apache.org/repos/dist/dev/fineract"; 
class="bare">dist.apache.org/repos/dist/dev/fineract</a> and add the following 
files in this new directory:</p>
 </div>
 <div class="ulist">
 <ul>
 <li>
-<p>apache-fineract-0.0.0-d003326e-binary.tar.gz.sha</p>
+<p>apache-fineract-0.0.0-a313bced-binary.tar.gz.sha</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-binary.tar.gz</p>
+<p>apache-fineract-0.0.0-a313bced-binary.tar.gz</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-binary.tar.gz.asc</p>
+<p>apache-fineract-0.0.0-a313bced-binary.tar.gz.asc</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-binary.tar.gz.md5</p>
+<p>apache-fineract-0.0.0-a313bced-binary.tar.gz.md5</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-src.tar.gz.sha</p>
+<p>apache-fineract-0.0.0-a313bced-src.tar.gz.sha</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-src.tar.gz</p>
+<p>apache-fineract-0.0.0-a313bced-src.tar.gz</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-src.tar.gz.asc</p>
+<p>apache-fineract-0.0.0-a313bced-src.tar.gz.asc</p>
 </li>
 <li>
-<p>apache-fineract-0.0.0-d003326e-src.tar.gz.md5</p>
+<p>apache-fineract-0.0.0-a313bced-src.tar.gz.md5</p>
 </li>
 </ul>
 </div>
@@ -6219,8 +7161,8 @@ It is important to create so called annotated tags (vs. 
lightweight) for release
 <div class="listingblock">
 <div class="content">
 <pre class="CodeRay highlight"><code data-lang="bash">% svn co <a 
href="https://dist.apache.org/repos/dist/dev/fineract/"; 
class="bare">dist.apache.org/repos/dist/dev/fineract/</a> fineract-dist-dev
-% mkdir fineract-dist-dev/0.0.0-d003326e
-% cp fineract/build/distributions/* fineract-dist-dev/0.0.0-d003326e/
+% mkdir fineract-dist-dev/0.0.0-a313bced
+% cp fineract/build/distributions/* fineract-dist-dev/0.0.0-a313bced/
 % svn commit</code></pre>
 </div>
 </div>
@@ -6242,7 +7184,7 @@ You will need your ASF Committer credentials to be able 
to access the Subversion
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep8 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep8 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -6260,7 +7202,7 @@ You will need your ASF Committer credentials to be able 
to access the Subversion
 <div class="ulist">
 <ul>
 <li>
-<p>Release candidates should be in format 
apache-fineract-0.0.0-d003326e-binary.tar.gz</p>
+<p>Release candidates should be in format 
apache-fineract-0.0.0-a313bced-binary.tar.gz</p>
 </li>
 <li>
 <p>Verify signatures and hashes. You may have to import the public key of the 
release manager to verify the signatures. (<code>gpg --recv-key &lt;key 
id&gt;</code>)</p>
@@ -6291,7 +7233,7 @@ You will need your ASF Committer credentials to be able 
to access the Subversion
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep9 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep9 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 <div class="admonitionblock caution">
@@ -6350,7 +7292,7 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep10 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep10 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -6443,7 +7385,7 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="text">% ./gradlew 
fineractReleaseStep11 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="text">% ./gradlew 
fineractReleaseStep11 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -6458,10 +7400,10 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="content">
 <pre class="CodeRay highlight"><code data-lang="bash">% svn co <a 
href="https://dist.apache.org/repos/dist/release/fineract"; 
class="bare">dist.apache.org/repos/dist/release/fineract</a> fineract-release
-% mkdir fineract-release/0.0.0-d003326e/
-% cp fineract-dist-dev/0.0.0-d003326e/* fineract-release/0.0.0-d003326e/
-% svn add fineract-release/0.0.0-d003326e/
-% svn commit -m &quot;Fineract Release 0.0.0-d003326e&quot; 
fineract-release/0.0.0-d003326e/</code></pre>
+% mkdir fineract-release/0.0.0-a313bced/
+% cp fineract-dist-dev/0.0.0-a313bced/* fineract-release/0.0.0-a313bced/
+% svn add fineract-release/0.0.0-a313bced/
+% svn commit -m &quot;Fineract Release 0.0.0-a313bced&quot; 
fineract-release/0.0.0-a313bced/</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -6473,7 +7415,7 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep12 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep12 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -6488,11 +7430,11 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="content">
 <pre class="CodeRay highlight"><code data-lang="bash">% git checkout develop
-% git branch -D 0.0.0-d003326e
-% git push origin :0.0.0-d003326e
+% git branch -D 0.0.0-a313bced
+% git push origin :0.0.0-a313bced
 % git checkout develop
-% git checkout -b merge-0.0.0-d003326e
-% git merge -s recursive -Xignore-all-space 0.0.0-d003326e  <i class="conum" 
data-value="1"></i><b>(1)</b>
+% git checkout -b merge-0.0.0-a313bced
+% git merge -s recursive -Xignore-all-space 0.0.0-a313bced  <i class="conum" 
data-value="1"></i><b>(1)</b>
 % git commit
 % git push $USER
 % hub pull-request</code></pre>
@@ -6512,7 +7454,7 @@ ${project['fineract.config.name']}</code></pre>
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep13 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep13 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 <div class="admonitionblock caution">
@@ -6621,7 +7563,7 @@ The Apache Fineract Team</code></pre>
 <div class="listingblock">
 <div class="title">Command</div>
 <div class="content">
-<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep15 -Pfineract.release.version=0.0.0-d003326e</code></pre>
+<pre class="CodeRay highlight"><code data-lang="bash">% ./gradlew 
fineractReleaseStep15 -Pfineract.release.version=0.0.0-a313bced</code></pre>
 </div>
 </div>
 </div>
@@ -116954,6 +117896,13 @@ Apache Fineract is a secure, multi-tenanted 
microfinance platform. The goal of t
 <td class="tableblock halign-left valign-top"></td>
 </tr>
 <tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">disallowExpectedDisbursements</p></td>
+<td class="tableblock halign-left valign-top"></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">Boolean</p></td>
+<td class="tableblock halign-left valign-top"></td>
+<td class="tableblock halign-left valign-top"></td>
+</tr>
+<tr>
 <td class="tableblock halign-left valign-top"><p 
class="tableblock">disbursementDetails</p></td>
 <td class="tableblock halign-left valign-top"></td>
 <td class="tableblock halign-left valign-top"><p class="tableblock">Set  of <a 
href="#GetLoansLoanIdDisbursementDetails">[GetLoansLoanIdDisbursementDetails]</a></p></td>
@@ -170473,7 +171422,7 @@ Apache Fineract is a secure, multi-tenanted 
microfinance platform. The goal of t
 </div>
 <div id="footer">
 <div id="footer-text">
-Version 0.0.0-d003326e<br>
+Version 0.0.0-a313bced<br>
 Last updated 2022-09-09 14:24:16 +0200
 </div>
 </div>

Reply via email to