Skip to content
Diogo RodriguesDiogo Rodrigues
Back to Work
Private Case StudyPlatform ArchitectureRBAC & AuthorizationClean Architecture

Internal Operations Platform

A multi-department professional services firm was running on disconnected spreadsheets, individual scripts, and manual workflows with no unified access model. I designed and built an internal platform that brought every operational domain — certificates, HR metrics, fiscal tooling, user management — under a single authorization layer, with clean architecture that lets the system grow without breaking.

TypeScriptNext.jsReactPrismaPostgreSQLPythonFastAPIDockerClean Architecture

The operational problem

The fiscal team tracked digital certificate expiry in a spreadsheet. HR pulled payroll metrics manually from the ERP. NFSe documents were retrieved ad-hoc, with no audit trail. IT managed user access through individual credentials — no central model, no permission hierarchy, no way to grant or revoke access without touching multiple systems.

Each department had built its own workaround. The workarounds worked — until someone left, a process changed, or the firm needed to onboard a new client. Then the fragility showed.

The additional constraint: the firm operated legacy ERP systems used by dozens of clients with years of operational data. Replacing them wasn't an option. New software had to integrate with what already existed — reading from the same data stores, surfacing the same operational information — without requiring ERP changes.

The engineering challenge was not just building the tools. It was designing a platform architecture that could hold all of this together cleanly, handle a non-trivial authorization problem, and remain maintainable as the organization's requirements kept evolving.

What I built

A modular internal operations platform — one unified system where each operational domain lives in its own independently structured module, accessed through a shared authorization layer, managed through a central admin interface. Adding a new module requires no changes to the authorization system or any existing module.

Admin panelUser management, role management, permission assignment, and platform configuration — separated from module-level operations.
Digital CertificatesCompliance monitoring for digital certificate validity: scans client environments, extracts metadata, validates expiry and integrity, surfaces status and risk through operational dashboards.
HR MetricsPayroll and HR operational indicators pulled from legacy ERP data and surfaced through web dashboards — replacing manual monthly exports.
Fiscal IntegrationAccounting reconciliation tooling connecting modern workflows to existing fiscal data sources.
NFSe FederalFederal service invoice retrieval, organization, and audit-ready report generation with full document traceability.

The authorization system

This was the design problem that determined the shape of everything else. The requirement wasn't simple role-based access. Some users needed role-based access (accountants get fiscal modules, HR staff get HR dashboards). Others needed individual overrides — a specific user granted certificate password management regardless of their role, without creating a new role just for that exception.

The naive approach — one role per permission combination — creates a permission explosion problem. Once an organization has fifty users and six modules with overlapping edge cases, the access model becomes impossible to reason about.

The model I designed has three layers:

Roles

Named permission groups assigned to users — for example, “Accounting Team” or “IT Admin.” Each role holds a set of capability codes that determine what the user can do across modules. Roles handle the common cases cleanly.

Module Capabilities

Each module defines its own capability codes: certificates.access, certificates.scan.run, certificates.password.reveal. Roles are granted combinations of capabilities across modules. New modules add their own codes — no changes to existing roles or the authorization logic.

Direct Grants

Individual users can receive specific capability overrides outside their role. This handles operational exceptions without creating a new role for every edge case. The access model stays clean instead of accumulating a role for every variation.

The resolved permission for any user is the union of their role capabilities and their direct grants. The authorization check is consistent across all modules — the same require-permission pattern applies everywhere, regardless of which module is being accessed.

Architecture decisions

Every module follows the same layer structure:

DomainEntities, value objects, port interfaces, domain errors. No dependencies on infrastructure or frameworks.
ApplicationUse cases as explicit classes — one per operation. DTOs for all outputs. Zero framework coupling.
InfrastructurePrisma repositories implementing the domain ports. In-memory implementations for testing.
PresentationNext.js Server Actions and API routes calling use cases directly. No business logic in route handlers.

Why this structure for an internal platform? Because internal platforms accumulate requirements. Authorization rules change. New modules get added. Compliance requirements evolve. A system where business logic is entangled with Prisma queries or Next.js route handlers becomes unworkable within months — not because the code is bad, but because the operational rules are genuinely complex and they change.

With this structure, each use case is independently testable without a database. The authorization module can be reasoned about in complete isolation from the certificate module. Swapping persistence layers affects only the infrastructure files.

Specific decisions worth noting:

  • In-memory repository implementations alongside Prisma repositories — not just for tests, but to keep use cases provably database-agnostic.
  • Value objects for AccountId, Email, Permission — preventing raw strings from propagating through the domain and making invalid states unrepresentable at compile time.
  • Explicit domain errors — InvalidCredentialsError, AccountDisabledError, AccessDeniedError — allowing use cases to fail with meaning rather than leaking HTTP status codes into business logic.
  • Password policy as a separate module — complexity rules and expiry logic in one place, invoked wherever needed without duplication.

The legacy integration constraint

The firm's existing ERP systems couldn't be replaced — they were used by dozens of clients with years of operational data. The platform had to read from what already existed, not own it.

This shaped several specific decisions:

  • A dedicated legacy-db module that encapsulates the read-only connection to the existing database. The platform queries it for operational data but never writes to it. This boundary is explicit in the architecture, not assumed.
  • The digital certificate module scans directories and file systems managed by the legacy ERP — it doesn't own that data, it surfaces it through the platform's compliance layer.
  • Module boundaries were designed around what the platform owns versus what it reads. Making that distinction explicit prevents the gradual drift toward tight coupling that happens when integrations are added informally over time.

Operational UX

An internal platform is only as valuable as the adoption it gets. If the interface requires training, it won't get used consistently — and inconsistent use means inconsistent data.

  • Permission-filtered navigation — after login, users see only the modules they have access to. No navigation items that lead to access-denied errors. The system presents the right surface, not the maximum surface.
  • Forced password change flow — new users are redirected to a required password change before accessing any module. Enforced at the middleware level, not left to trust in the application layer.
  • Compliance dashboards — the certificate module surfaces expiry status, validation results, and items needing immediate attention in a way that makes risk visible at a glance. Operational teams can review status without reading raw data.
  • Isolated admin section — platform administration is separated from module operations, with its own access control. The people managing user permissions don't need to navigate through operational tools to do it.

What this project required

This is not the kind of problem you can solve with good framework knowledge. Understanding why the authorization model needs three layers, what breaks when you couple business logic to infrastructure, or how to design a legacy integration boundary — that comes from working inside the operational domain, not just reading about it.

Five years inside accounting and professional services firms gave me the context to design this system from the constraint inward, not from the framework outward. The clean architecture isn't academic preference. The RBAC design isn't over-engineering. They're the shapes that the actual operational requirements demanded.

Confidentiality

This project is documented as a private case study. Source code, client data, business configurations, and company-specific details are not included. The architecture decisions, authorization model, and system design documented here reflect the real system — names and operational specifics have been generalized.