Project / Case study

Work Fusion

An ISO-oriented workflow platform for tasks, approvals, evidence, and audit history. I designed the event-driven backend so committed workflow changes remained traceable across broker, network, and service failures.

Work Fusion cover showing the logo, a short product description, and mobile workflow screens for task execution, approvals, and monitoring.
FIG 01 — The Work Fusion cover pairs the product identity with the original mobile task, approval, and monitoring screens.

Digitising work that needs to remain traceable

Work Fusion digitised ISO-oriented workflows that previously depended on disconnected documents, spreadsheets, chat messages, and storage folders. The product connected SOP and form management, task execution, approvals, evidence, notifications, corrective actions, centralised documents, and audit history in one system.

The system was designed around the documented-information and traceability needs represented by ISO 9001 Clauses 7.5 and 9.2 and ISO/IEC 27001 Annex A.8.16 and A.5.37. It did not claim a production certification or a real operational ISO audit.

This was a three-person Applied Bachelor's final project for the Informatics Engineering programme at Politeknik Elektronika Negeri Surabaya. We developed the product together, while my work focused on the backend architecture and the reliability of its audit-event path.

Work Fusion dashboard and mobile workflow screens for offline task execution and ISO-oriented approvals.
FIG 02 — Work Fusion connected mobile execution, approvals, monitoring, and audit-oriented task management in one workflow.

A final status was not enough for an audit

A conventional database can show the current state of a task, but that state does not explain who changed it, when it changed, or which approval steps produced it. Work Fusion needed a chronological audit trail that could answer those questions without relying on logs scattered across separate services.

The distributed architecture introduced another failure case. A workflow change could be committed successfully while its audit event failed to reach the message broker. The backend therefore needed to keep the event recoverable after the API request had already returned, while preventing retries from recording the same event twice.

  • Preserve chronological evidence for selected ISO requirements.
  • Recover events after broker, network, or service failure.
  • Prevent duplicate audit records during retry.
  • Enforce access boundaries across different operational roles.
  • Remain testable on limited academic infrastructure.

Four roles, different definitions of a complete workflow

The product had to work for people who were solving different parts of the same controlled process. We defined the workflow around four recurring operational perspectives before deciding how data should move through the backend.

Admin managed forms, users, roles, permissions, and controlled changes. Supervisor monitored progress, approvals, overdue work, and evidence readiness. Field executor completed assigned work and submitted evidence, including in unstable network conditions. Auditor needed access to structured history, documents, evidence, and responsibility.

Admin persona describing form management, access control, and document-version needs.
FIG 03 — Admin persona focused on controlled forms, access boundaries, and document-version management.
Supervisor persona describing task visibility, overdue notifications, and evidence tracking.
FIG 04 — Supervisor persona prioritised approval visibility, overdue work, and evidence readiness.
Field executor persona describing simple offline task completion and reliable synchronisation.
FIG 05 — Field executor persona required simple task completion in unstable network conditions.
Auditor persona describing access to structured historical records, evidence, and audit trails.
FIG 06 — Auditor persona depended on structured records, evidence, and chronological audit history.

My responsibility was the audit-event path

We shared the product planning and integration work. Alga Vania Salsabilla developed the mobile interface and connected user-facing features to the system services. Rakha Putra Pratama designed the application flow and interface and supported application testing.

I designed and implemented the backend architecture. My scope covered logical command-query separation with MediatR, synchronous validation through gRPC, asynchronous publication through NATS JetStream, event-based audit logging, the transactional outbox, four layers of idempotency, access-control behaviour, and the tests used to verify integrity, recovery, and performance.

Separating the request path from event delivery

ASP.NET Core services handled business operations and stored transactional data in PostgreSQL. Services used gRPC when a result was required immediately and NATS JetStream for audit events and notifications that could be processed asynchronously. Keycloak handled identity and access management, while Docker kept the environment reproducible for implementation and testing.

The backend separated the business request path from the eventual delivery of audit events. A committed workflow change wrote both business data and a pending audit event inside one database transaction, then a background publisher delivered the event to NATS JetStream and AuditTrailService recorded the final chronological log.

  1. Client request
  2. ASP.NET Core service
  3. One database transaction
    Business data
    Pending audit event in the outbox
  4. Background outbox publisher
  5. NATS JetStream
  6. AuditTrailService
  7. Chronological audit record

Selected critical inter-service validation uses gRPC before the transaction proceeds.

FIG 07 — The business change and pending audit event are stored before asynchronous publication.

The decisions that protected the audit trail

Hybrid communication. I used gRPC for service calls that needed an immediate validation result and NATS JetStream for audit events and notifications. This kept critical checks explicit while allowing event consumers to process work independently. The trade-off was operating and observing two communication paths instead of one.

Logical CQRS with MediatR. I separated commands from queries through MediatR handlers while keeping a shared transactional database. This made write intent and read intent easier to test without adding the operational cost of separate read and write stores.

Transactional Outbox. Each service stored the business change and its pending audit event in the same database transaction. A background publisher delivered the event later. This added an outbox table, polling, retry behaviour, and operational states, but removed the gap where a committed workflow could permanently lose its audit event.

Four-Layer Idempotency. Retries were expected, so duplicate prevention was applied in layers: the outbox persisted the event with the business change, each event received a deterministic EventId, NATS JetStream deduplicated messages inside its configured window, and the audit log database enforced uniqueness again at insert time. The trade-off was more operational detail to manage, but recovery no longer depended on a single duplicate check.

Role-Based Access Control. Access decisions were modelled around reusable roles, profiles, and permissions. This allowed the backend to permit operational actions while restricting form administration, approvals, and audit-log access to authorised roles.

Work Fusion mobile screens showing successful offline synchronisation and task approval confirmation.
FIG 08 — The mobile workflow needed to keep offline execution, synchronisation, and approval states legible to operational users.

The event path was tested under partial failure

Functional and authorisation tests checked the expected workflow and access boundaries. The reliability evaluation then introduced failures into the event-publication path rather than treating a successful API response as sufficient evidence.

The resilience testing covered three partial-failure scenarios: stopping NATS JetStream while services kept accepting operations, interrupting service-to-NATS connectivity while the service stayed available, and forcing a service crash after pending events had been written to the outbox. After each recovery, SQL checks verified audit-event counts, duplicate EventId values, and pending outbox entries. K6 provided repeatable integrity and performance workloads.

What the tests verified

The tests showed that events already stored in the outbox remained recoverable across the three injected failures and were not recorded twice in the end-to-end verification.

Under the 80-user workload, every workflow completed, but the overall p95 latency reached 5,615 ms and exceeded the 5,000 ms target. That result pointed to synchronous multi-step TaskService operations and limited infrastructure capacity as the main trade-off, not to the audit-trail path itself.

GroupMetricResult
Audit integrityWorkflows946
Audit integrityAudit events6,622
Audit integrityAudit Completeness Rate100%
Audit integrityDuplicate Processing Rate0%
Audit integrityFinal Outbox Pending Rate0%
Failure recoveryPartial-failure scenarios3
Failure recoveryMessage Loss Rate0%
Failure recoveryBroker-outage entries recovered20
Failure recoveryNetwork-interruption events recovered10 in 0.304 seconds
Failure recoveryService-crash events recovered10 in 0.298 seconds
Failure recoveryFinal pending entries0
PerformanceVirtual users80
PerformanceSuccessful workflows1,025
PerformanceThroughput4.23 workflows per second
PerformanceWorkflow success rate100%
PerformanceHTTP error rate0%
PerformanceOverall p99 latency7,243 ms
PerformanceOverall p95 latency5,615 ms

What the evaluation did not prove

The results describe a controlled staging environment, not a production certification. The system ran on a 2 vCPU, 4 GB RAM VPS and the load test stopped at 80 virtual users.

The study did not include an end-user usability evaluation, a real operational ISO audit, a database-failure scenario, a multi-node infrastructure failure, or a production-scale cloud deployment.

The p95 result also showed that reliable event handling does not remove performance work. Multi-step task operations and limited infrastructure still need profiling and optimisation at higher load.

Reliability continues after the response returns

This project changed how I evaluate a successful request. Returning a 2xx response confirms one stage of the workflow, but it does not confirm that every downstream record is durable, recoverable, and safe to process again.

The outbox made failure visible as a pending state instead of silent data loss. Idempotency made recovery safe. The remaining latency reinforced a second lesson: reliability and performance need separate evidence, and improving one does not automatically prove the other.