Back to Projects
Internal System
PT. Noxus Ideata Prima

BUMN CRM Backend Services

Enterprise CRM microservices platform for a major state-owned enterprise (BUMN), migrating from a legacy billing system with an approval-workflow-driven architecture.

Java
Spring Boot
Spring Security
Oracle DB
Apache Kafka
Microservices

Overview

Backend services for a large state-owned enterprise (BUMN), built as part of PT. Noxus Ideata Prima. The system is an enterprise Customer Relationship Management (CRM) platform, built to replace a legacy billing system with a modern, modular architecture — covering account management, user management, and product & promo management.

flowchart TB
    subgraph Client["Client"]
        FE["React SPA<br/>single shell for all business modules"]
    end

    subgraph Gateway["Reverse Proxy (Nginx)"]
        NG["Route by path prefix<br/>per business domain"]
    end

    subgraph Services["Business Microservices (Spring Boot)"]
        ACC["Account Management Service"]
        UM["User Management Service"]
        PP["Product & Promo Service"]
        NOTIF["Notification Service"]
    end

    subgraph Shared["Shared Libraries (Maven)"]
        COMMON["Common Library<br/>security, JWT/LDAP, approval engine, notification client"]
        CRM["Common CRM Library<br/>JPA entities + repositories for the whole schema"]
        BASE["Common Base Library<br/>base entity, generic filter/spec framework"]
    end

    subgraph Infra["Infrastructure"]
        ORA[("Oracle DB")]
        KAFKA{{"Kafka<br/>notification topics"}}
        MINIO[("Object Storage<br/>attachments")]
        LDAP[("LDAP / Active Directory")]
    end

    FE --> NG
    NG --> ACC & UM & PP & NOTIF

    ACC --> COMMON & CRM
    UM --> COMMON & CRM
    PP --> COMMON & CRM
    CRM --> BASE

    ACC & UM & PP & CRM --> ORA
    ACC & UM & PP -->|publish event| KAFKA --> NOTIF
    NOTIF -->|real-time push| FE
    ACC & UM & PP --> MINIO
    UM --> LDAP

Each business service is deployed and scaled independently (its own Helm chart on Kubernetes), yet all three share a single Oracle schema through a common JPA library instead of defining their own entities — keeping the data model and most security/approval logic in one place rather than duplicated per service.

Problem

  • The legacy billing system was rigid and hard to extend as the business grew.
  • Sensitive data (accounts, pricing, promos, contracts) needed a traceable, multi-level approval process before any change could go live.
  • Years of legacy billing data had to move into the new system without disrupting day-to-day operations.

Solution

  • Modular services, each responsible for one business area (accounts, users, product & promo), so teams can build and release independently.
  • Built-in approval workflow: every sensitive change is routed through the right approver before it takes effect, with a full audit trail.
  • Real-time notifications so approvers and requesters are informed the moment a decision is needed or made.
  • Staged data migration that moved legacy records into the new system safely, flagging anything that needed manual review instead of blocking the process.

Approval Workflow (shared across every module)

sequenceDiagram
    participant U as User
    participant SVC as Business Service<br/>(Account/Product/User)
    participant APR as Approval Engine<br/>(Common Library)
    participant DB as Oracle DB
    participant KAFKA as Kafka
    participant NOTIF as Notification Service
    participant APV as Approver

    U->>SVC: Submit change
    SVC->>DB: Save/update business record (pending)
    SVC->>APR: Register approval request
    APR->>DB: Insert approval record (WAITING_FOR_APPROVAL)
    APR->>KAFKA: publish notification.create
    KAFKA->>NOTIF: consume event
    NOTIF-->>APV: real-time notification
    APV->>SVC: Open record detail
    SVC->>APR: Check if current user is the approver
    APR-->>SVC: true / false
    SVC-->>APV: Show approval detail (only if authorized approver)
    APV->>SVC: Approve / Reject
    SVC->>DB: Update approval history + record status
    SVC->>KAFKA: publish notification.broadcast (decision)
    KAFKA->>NOTIF: consume
    NOTIF-->>U: notify requester of decision

The same engine handles approvals for every domain — the current-level approver is resolved by matching the position required by the approval hierarchy against the requesting/approving user's own position, so a user isn't shown the approval action unless they're the authorized approver at that level.

Simplified Data Model

erDiagram
    CUSTOMER ||--o{ ACCOUNT : "owns"
    ACCOUNT ||--o{ SERVICE_AGREEMENT : "has"
    ACCOUNT ||--o{ PROMO : "receives"
    PRODUCT ||--o{ PRODUCT_VERSION : "versioned (copy-on-write)"
    PRODUCT_VERSION ||--o{ PRICING : "priced"
    PRODUCT_VERSION ||--o{ TERMS_OF_SERVICE : "governed by"
    EMPLOYEE ||--o{ POSITION_ASSIGNMENT : "assigned"
    POSITION_ASSIGNMENT }o--|| POSITION : "holds"
    POSITION ||--o{ APPROVAL_HIERARCHY : "approval level"
    APPROVAL ||--o{ APPROVAL_HISTORY : "decision trail"
    ACCOUNT ||--o{ APPROVAL : "requires approval (via category)"
    PRODUCT_VERSION ||--o{ APPROVAL : "requires approval (via category)"
    PROMO ||--o{ APPROVAL : "requires approval (via category)"

The links into APPROVAL are conceptual rather than literal foreign keys — the approval table stores a generic record ID plus a category column identifying its originating domain, shared by every business module. Products follow a copy-on-write versioning pattern: changes create a new product-version row instead of mutating the existing one.

Key Contributions

  • Built core API endpoints for account, service agreement, and pricing/promo management.
  • Resolved data-sync issues between services sharing the same underlying data.
  • Contributed to the legacy-to-new data migration effort.
  • Delivered within enterprise-grade security and code-quality standards required for a BUMN client.