Architecture
1.
Technology stack
- Kotlin + Android Views/XML + ViewBinding
- Gradle Wrapper
9.3.1
- Android Gradle Plugin
9.1.0
- Firebase Auth + Firestore
- WorkManager for periodic sync and notifications
- Glide for profile image loading
2.
High-level architecture
Njangui uses a client-centric layered architecture:
- View layer (
views/)
- Activities, dialogs, user interactions, UI state.
- Port contracts (
providers/ports/)
- Business-facing interfaces.
- Port implementations
(
providers/ports/impl/v1/)
- Firestore/Auth implementations and domain workflows.
- Domain models (
providers/models/)
- Serializable business structures.
- Utilities (
utils/)
- permissions, validation, formatting, notification processors.
- Background runtime (
workers/,
receivers/)
- periodic lifecycle sync and notification delivery.
The Providers class is used as a lightweight service
locator with lazy setup.
3.
Runtime components
NjanguiApp
- applies persisted theme mode,
- schedules notification worker:
- periodic pull every 15 minutes,
- immediate one-shot run on startup.
BootReceiver
- re-schedules worker after reboot/update.
NotificationPullWorker
During each run, it executes: 1. tontine lifecycle sync, 2.
governance lifecycle sync, 3. penalties lifecycle sync, 4. caisses
lifecycle sync, 5. events lifecycle sync, 6. unread notification pull
and local delivery.
4.
Data persistence model
Primary Firestore collections:
users
notifications
tontine-groups-basic
tontine-groups-caisses
tontine-groups-elections
tontine-groups-events
tontine-groups-tontines
tontine-groups-penalties
Local settings (SharedPreferences via
AppSettingsPortImpl): - language, - theme mode, - default
active group, - last pulled notification id.
5.
Permission architecture
Rights enum: - MANAGE_MEMBERS -
MANAGE_TONTINES - MANAGE_CAISSES -
MANAGE_PENALTIES - MANAGE_EVENTS
Permission decisions are centralized in
PermissionsChecker and depend on: - active membership, -
active staff mandate, - staff-right ownership, - governance fallback
windows for creator/admins.
6.
Key domain structures
TontineGroupBasicModel: group identity, members,
admins, staff
TontineModel: participants, ranking, rounds, lifecycle
flags
RankingPreConditionsModel: strategy, fixed positions,
ranking mode, deadline
CaisseModel: individual/shared balances, transactions,
low-balance policy
GroupEventModel / MainLeveeModel: event
and main levee workflows
PenaltyModel: disciplinary lifecycle with
recoveries
FinancialReportModel: aggregated finance output
7.
Compatibility strategy
Because staging carries real usage and historical schema variations,
implementations follow tolerant read/write behavior:
- nullable fields and defaults in models,
- defensive parsing for dates/enums,
- no-arg constructors preserved for Firestore model mapping,
- reconstruction of missing notification document fields where
needed,
- migration-safe behavior over destructive rewrites.
8.
Architectural invariants
- Business decisions are not encoded in UI widgets only.
- Rights checks happen before every sensitive mutation.
- Accounting mutations generate transaction traces.
- Reports are derived from filtered datasets, not hidden global
state.
- Notification delivery must avoid duplicate and wrong-recipient
exposure.
Core
Workflows
This section documents the most critical domain flows and their
invariants.
Visual map (Mermaid)
flowchart TD
A[User Action] --> B{Permission Check}
B -->|Denied| C[UI Feedback + Stop]
B -->|Granted| D[Port Impl Mutation]
D --> E[Firestore Write]
E --> F[Lifecycle/Derived Effects]
F --> G[Notification Persisted]
G --> H[Worker Pull]
H --> I[Local Device Notification]
F --> J[Report Aggregation Inputs]
1.
Authentication and account eligibility
sequenceDiagram
participant U as User
participant L as LoginActivity
participant A as AuthPort
participant F as FirebaseAuth
participant D as UserDocument
U->>L: Submit credentials
L->>A: login(email, password)
A->>F: signIn
F-->>A: auth success/failure
A->>D: fetch business user state
D-->>A: account status
A-->>L: allow or deny
Invariants: - Firebase authentication success is necessary but not
sufficient. - Business account state must also allow access.
2.
Group context synchronization
ProtectedBaseActivity
-> resolve default group
-> verify active membership
-> update local settings
-> populate drawer switcher and screen
Invariants: - all module operations use the active group context, -
stale group context must be corrected before mutating data.
3.
Permission resolution
Centralized in PermissionsChecker.
Decision factors: - active membership, - active staff mandate, -
delegated staff rights, - super-admin fallback windows after mandate
expiry.
Invariants: - no sensitive mutation without explicit permission
check, - MANAGE_MEMBERS has dedicated fallback logic unlike
other rights.
4.
Governance lifecycle
Election
stateDiagram-v2
[*] --> DraftElection
DraftElection --> VotingOpen: create + notify voters
VotingOpen --> Consolidation: all votes or deadline reached
Consolidation --> NewStaffActive: apply results
NewStaffActive --> [*]
Staff replacement
Request replacement
-> collect approvals from eligible staff
-> apply replacement once threshold met
-> notify group
Invariants: - one active mandate window at a time, - replacements
follow approval rules before effective application.
5.
Tontine ranking lifecycle
Configuration
RankingPreConditions includes: -
multipleNamesStrategy: NONE or
BALANCED - fixed positionning -
rankingType: PARTICIPANT_SELECTION or
RANDOM_ALGORITHM - participant-selection deadline
Execution and closure
flowchart LR
A[Configure preconditions] --> B[Participant picks or algorithm picks]
B --> C{Missing slots?}
C -->|Yes| D[Auto-complete on closure/deadline]
C -->|No| E[Finalize ranking]
D --> E
E --> F[Notify ranking changes]
Invariants: - rule updates can invalidate existing picks, - fixed
positions have priority, - closure may auto-fill missing picks.
6.
Round lifecycle (tontine)
round starts -> notify all participants
-> contributions captured (draft/final)
-> outflows specified for beneficiaries
-> close round
-> generate penalties if needed
-> archive successful round
-> notify completion
Invariants: - round closure requires MANAGE_CAISSES, -
contribution and outflow payloads must be coherent, - ending condition
is automatic when all rounds are completed.
7.
Caisse accounting workflow
Core model
All monetary effects are represented through transactions: -
direction: credit/debit, - typed references
(referenceType, referenceId), - optional
counterparty and note metadata.
Shared caisse distribution
distribute shared caisse amount
-> validate shares (amount/percent)
-> create debit from source
-> create credits/outflows by destination
-> persist and report
Invariants: - accounting operations must be balanced by design, -
deletion of caisse must remove its accounting transactions.
8.
Events and main levees
Events
Supports: - individual contribution mode with caisse priorities, -
collective contribution mode from shared caisses, - optional external
beneficiaries at creation, completed at closure if needed.
Main levees
Supports: - compulsory/optional campaigns, - progressive contribution
capture, - closure with sanction generation on rule violations.
Invariants: - outflow closure checks required amounts and eligible
caisse types, - rollback paths exist for incorrect outflow
definitions.
9. Penalties lifecycle
stateDiagram-v2
[*] --> PendingValidation
PendingValidation --> Validated
Validated --> Paid
Paid --> PaidPendingRecovery
PaidPendingRecovery --> RecoveryClosed
PendingValidation --> Cancelled
Validated --> Cancelled
Invariants: - penalties may include amount and text fines, - amount
fines require explicit recovery destination and reason where applicable,
- disciplinary and financial reports must include recovery effects.
10. Notifications lifecycle
Notification flow:
sequenceDiagram
participant B as Business Mutation
participant N as Notifications Doc
participant W as NotificationPullWorker
participant P as NotificationTypesAndProcessors
participant D as Device NotificationManager
B->>N: append notification payload
W->>N: pull unread notifications
W->>P: process by type
P->>D: build + display local notification
Invariants: - recipient targeting must be strict, - duplicate
delivery must be prevented via deliveredTo and local max-id
tracking, - missing legacy fields should be reconstructed when
possible.
11. Reporting workflow
Finance reports
Sources include: - rounds, - caisses, - events/main levees, -
penalties and recoveries, - internal/external beneficiary flows.
Penalty reports
Must expose: - state transitions, - paid vs unpaid, - context
(group/tontine/round), - due dates and closures.
Invariant: - exports are generated from the currently filtered
dataset only.
Testing
and Quality
1.
Quality objectives
Njangui quality is defined by: - business-rule correctness, -
permission safety, - accounting consistency, - schema compatibility, -
stable notification delivery.
2.
Automated tests (current baseline)
Representative unit test coverage includes: - validators and string
helpers, - permissions and governance logic, - identity rendering
helpers, - caisse accounting invariants.
Known classes: - FieldValidatorTest -
StringUtilsTest - PermissionsCheckerTest -
UserIdentityUtilsTest -
CaisseAccountingUtilsTest
Run tests:
./gradlew :Njangui:testDebugUnitTest
3.
Recommended test matrix for new features
- Rights matrix
- member vs staff vs admin behavior,
- mandate active/expired cases.
- Lifecycle matrix
- creation/update/closure paths,
- rollback and retry behaviors.
- Schema matrix
- complete documents,
- partially missing fields,
- legacy values and enum fallbacks.
- Accounting matrix
- debit/credit balance effects,
- shared vs individual caisse,
- recovery destination scenarios.
- Notification matrix
- correct recipient targeting,
- duplicate prevention,
- background + foreground behavior.
4.
Manual QA high-risk scenarios
- ranking rule update after participant picks,
- round closure with partial contributions and redirection,
- event collective outflow with shared caisse constraints,
- penalty paid -> recovery closure workflow,
- language/theme switch while on Settings and other active
screens,
- report filters followed by export.
5.
Logging and diagnostics
When diagnosing: - capture stack trace + timestamp, - include user
role and active group, - include workflow step and expected state
transition, - include relevant Firestore document ids.
6.
Documentation quality gate
Docs pipeline validates: - Pandoc + TeX availability, - required
LaTeX style packages, - PDF and HTML generation, - publishable artifact
under docs/build.
7.
Definition of done (quality)
A feature is done when: 1. behavior is correct across
rights/lifecycle constraints, 2. data compatibility is preserved, 3.
regression-prone paths are tested, 4. logs/alerts are actionable, 5.
documentation and changelog are updated.
Backend
v1.2 Public Release Foundation
Scope
This chapter documents the v1.2 foundation delivered so far: -
migration path from Firebase adapters to a public backend, - modular
monolith (hexagonal style, same principles as MBOAGEB), - identity
server baseline, - subscription and sponsor model linked to Njangui
groups, - public API deployment + admin dashboard deployment.
Repository layout
The repository is now split as: - src/frontend/mobile/
for the Android app, - src/frontend/dashboard/ for the
admin back-office, - src/backend/ for the public backend, -
deployment/ for centralized docker/nginx deployment assets,
- docs/ as the single documentation source at
repository root.
Architecture style
The backend uses a modular monolith with strict layering: -
domain - application -
infrastructure
Current top-level backend modules: - kernel -
common - gateway - identity -
subscription - njangui (business bounded
contexts scaffolded) - njangui-application (runtime
assembly) - tests
Public domains and hosting
Public domains (same machine, Nginx host-based routing): - API demo:
demo.njanguiapp.com - API prod:
api.njanguiapp.com - Admin demo:
demo-admin.njanguiapp.com - Admin prod:
admin.njanguiapp.com
Identity baseline
Implemented: - account registration/login/refresh token flow, - JWT
access + refresh strategy, - stateless security filter, - bootstrap
platform admin account, - password reset full flow (expirable link +
backend confirmation + landing redirection), - connected device/session
listing and revocation, - MFA support: - email OTP challenge
(configurable), - TOTP setup/activation with authenticator apps.
Public auth endpoints: -
POST /api/v1/public/identity/register -
POST /api/v1/public/identity/login -
POST /api/v1/public/identity/refresh -
GET /api/v1/public/identity/ping -
POST /api/v1/public/identity/password-reset/request -
GET /api/v1/public/identity/password-reset/confirm -
POST /api/v1/public/identity/password-reset/complete
Authenticated identity security endpoints: -
GET /api/v1/identity/sessions -
DELETE /api/v1/identity/sessions/{sessionId} -
DELETE /api/v1/identity/sessions -
POST /api/v1/identity/mfa/totp/setup -
POST /api/v1/identity/mfa/totp/activate
Login response behavior: - 200 OK with tokens when
authentication is fully completed, - 202 Accepted with
status metadata when MFA step-up is required
(EMAIL_OTP_REQUIRED or TOTP_REQUIRED).
Gateway and endpoint protection baseline
Implemented gateway-inspired foundations (aligned with MBOAGEB
principles): - centralized request locale resolution using request
filters (X-Locale then Accept-Language
fallback), - thread-local locale holder exposed to all modules through a
resolver component, - standardized client context extraction (request
id, device/session/client headers, user-agent, resolved client IP), -
request-id propagation through response headers, -
@GatewayAction metadata + interceptor-based endpoint-level
protection checks.
These foundations coexist with Spring Security JWT enforcement for
authenticated routes.
API
discoverability (Swagger/OpenAPI)
OpenAPI is now explicitly configured and integrated. - API docs JSON:
/v3/api-docs - Swagger UI:
/swagger-ui.html
Profile behavior: - dev and demo: enabled -
prod: disabled by default
Identity and contact endpoints are now annotated with OpenAPI
operation metadata for precise endpoint documentation.
Subscription model (group-linked)
Implemented capabilities model linked to each Njangui group: -
maxMembers - maxTontines -
maxNamesPerParticipant - maxCaisses -
outflowRedirectionEnabled -
autoPenaltiesEnabled
Supported plans: - FREE (default fallback for every
group) - PREMIUM-MONTHLY (500 XAF/month) -
PREMIUM-YEARLY (5000 XAF/year)
Current baseline values: - Free: members=25, tontines=25,
names/participant=1, caisses=3, redirection disabled, auto penalties
disabled. - Premium: limits unlimited (null-based), redirection enabled,
auto penalties enabled.
Resolution priority: 1. active sponsor grant for the group/date, 2.
active paid group subscription, 3. default free plan.
Admin dashboard (React)
Initialized in src/frontend/dashboard/: - React + Vite
runtime, - Nginx static serving in Docker, - environment-aware
deployment (demo/prod), - live read of public
subscription plans endpoint.
Data storage and migrations
Database: - PostgreSQL
Migrations: - Liquibase from day one, - baseline tables for identity
+ subscription + sponsor cards + grants.
Mail infrastructure
Configured external infrastructure: - SMTP:
smtp.hostinger.com:465 (SSL/TLS) - IMAP:
imap.hostinger.com:993 (SSL/TLS) - POP:
pop.hostinger.com:995 (SSL/TLS)
Official contact: - contact@njanguiapp.com
Tests
Added tests in the same spirit as the MBOAGEB backend service-level
validation: - IdentityAuthenticationServiceTest -
SubscriptionManagementServiceTest
Current CI-local verification used during this iteration: -
mvn -q -DskipTests compile (backend) -
mvn -q test (backend) - npm run build
(dashboard)