Backend and infrastructure for a platform in production
No internal material
CompanyMas Que Bien Service
RoleBackend developer
PeriodJanuary 2026 – present
Engagementemployee, under contract
Fronts3 (backend · registration · panel)
StackJava · Spring · Postgres · AWS
I\'m the person responsible for the backend and the infrastructure of a platform in production at Mas Que Bien Service: a user app, a professional app, and a panel for the internal team. What you won\'t find here is code, screenshots, real topology or internal product names — that belongs to the company. What\'s left is how I solved each thing.
What I actually did
I designed and built the backend architecture: separation by context, a database per module, the API entry layer, file storage, caching, external integrations behind abstractions, the technical documentation, infrastructure as code, and the cloud deployment.
Experience file · piece 01 of 06Context
01What an operational platform needs
A platform with three faces — users, professionals, an internal team — needs a lot of pieces underneath: a modular backend, clear APIs, external integrations, files, business rules, an admin panel. And each piece has to change without breaking the rest.
My work covered the three fronts that hold that up: the backend and its infrastructure, provider registration, and the internal panel. In all three I worked the same way — the API contract first, validation on the server, and every decision written down with its reason.
Backend & infrastructureSeparation by context, a database per module, API entry layer, caching, and file storage.
Rules & integrationsBusiness logic validated on the server, and external providers behind abstraction layers.
Documentation & deploymentOpenAPI contracts, decisions written down, Terraform, Docker, tests, and deployment on AWS.
The problem. The platform had to split responsibilities, make deploys cheap, organise data by domain, and take on external providers without welding the core logic to any of them.
What I did. A modular backend with services separated by responsibility, a database per module, an API entry layer, caching, file storage, technical docs, and the infrastructure written as code.
Annex A · the platform, seen from far away.
How the backend is split
I split it by context, not by technical layer. Every context carries the same three folders: web with controllers and DTOs, which doesn't know the database exists; application with the use cases and the rules; infrastructure with JPA, storage and config. I copied the shape of the microservices we'd migrate to later, so the move would be mechanical.
File storage goes in through one interface. In production the files live in Amazon S3; on my machine I run MinIO in Docker against that same interface. Three config properties change and the code stays put. In the database I keep the file's key, never the bytes.
Annex B · one context up close, and the seam where the provider gets swapped.
The sheet I fill in before deploying
I don't call a deploy good until every box says where it comes from. The right-hand column is the one that costs work and the one that's worth it.
Check
Evidence
The image works in any environment
the Dockerfile doesn't pin the profile: whoever starts it decides
The port comes from the environment
one config line, with a default for development
Credentials and endpoints by variable
database, storage, integrations and admin — nothing in the repo
The health check stays open on purpose
integration test: it answers 200 with no credentials
The signed webhook gets in without a login
valid signature 200, anything else doesn't pass
The internal panel, closed
401 with no credentials · 200 with the right ones · 401 with the wrong ones
Pasted from the terminal
86 unit tests · 18 integration tests · 0 failures three branches in parallel, merged in one go — the conflict I expected in the config never happened, and I still checked it by hand
How the services are laid out
Seven services, one doorSeven backend services, each running as its own ECS Fargate task and owning its database — all on a single RDS instance, so the isolation doesn't multiply the bill. Client, provider and staff apps come in through one gateway that checks the token and routes.
One role per serviceEvery service assumes its own IAM role with only the permissions it needs: most touch nothing outside their own data, and only the one that runs document checks reaches the vision and OCR services. If one is compromised, the blast radius stays inside it.
No keys in the repositorySecrets live in Secrets Manager and each task gets temporary credentials at runtime through STS. Configuration comes from the environment, so the same image runs in every environment without a rebuild.
Two regions, on purposeThe system runs in the cloud region closest to its users. Only the document-and-face checks cross to a second region, because the managed services they need aren't offered in the first one — about 150 ms round trip, paid by that call alone and not by the rest of the platform.
Matching by distance, and a price that stops moving
One of the services matches requests to nearby providers using geospatial queries in the database, and offers the job in waves: small batches, ordered by a scoring rule, each with a deadline to accept. When a request is confirmed the price, tax and commission are frozen into an immutable snapshot — a rate change tomorrow never rewrites what was already agreed. I took part in defining that structure and its flow.
Modular designREST APIsPorts and adaptersPer-module persistenceMigrationsConfig per environmentInfrastructure as codeCloud deployment
Experience file · piece 03 of 06Front 2 · registration
03Registration, documents and identity checks
APIs, states and files
The problem. A guided sign-up has to save progress, validate on the server, take files, and expose a contract clear enough for the frontend to move without asking.
What I did. The endpoints and the rules behind them: a draft that survives a closed tab, the required documents worked out from the professions the person picks, states with their history, uploads with type and size limits, and errors with stable codes instead of a generic 500. The screens were built by someone else on the team; the shared contract let us work in parallel.
Draft→Submitted→Verifying→Approved·Sent back
Every jump is written down with who caused it and when. Nothing changes state without leaving a trace.
Identity, on the other side of a wall
Identity verification runs with an external provider, and the platform talks to it through one interface. The provider's answer comes in by webhook, signed; if the signature doesn't check out, the request doesn't get in. What the provider returns is copied into our own storage in two steps, so a half-finished copy rolls back instead of leaving a record pointing at nothing.
Phone numbers are normalised before they're stored, ages are validated on the server, and duplicate emails come back as a business error with its own code — not as a crash.
State modellingServer-side validationUpload and validationDrafts with expiryOpenAPI contractsSigned webhooksTwo-phase copy with rollbackError catalogue
The problem. A platform with users and professionals generates internal work: records and documents somebody has to review, a catalogue to keep up to date, and settings that shouldn't need a deploy to change.
What I did. The backend under that work. A review queue with temporary leases, so several reviewers can pull from it at once without stepping on each other: each one claims a batch, and whatever isn't finished in time goes back to the queue instead of getting lost. Role-based access with an identity plane separate from end users, a catalogue and settings the admin edits from the panel, and an audit trail with before and after values.
Annex C · reviewers pull in parallel, and nothing falls off the list.
Documents never leave through the front door
The panel doesn't reach storage directly. It asks the backend, the backend checks who's asking and serves the file itself. The store is never exposed to the browser — one less door, and every download passes through the same check as everything else.
Java · Spring Boot — PostgreSQL — queues · asynchronous work — roles · auditing
Experience file · piece 05 of 06Inventory
05What I used, and what for
Backend
Java · Spring Boot
REST APIs · OpenAPI
Validation · DTO mapping
Data
PostgreSQL · PostGIS
A database per module
Modelling · versioned migrations
Geospatial queries
Cloud & infrastructure
AWS · Docker
Terraform
Config per environment
ECS Fargate · one IAM role each
Secrets Manager · STS temporary credentials
Integrations
External APIs · signed webhooks
Object storage
Providers behind an interface
Federated identity · staff tokens
Payments behind swappable adapters
Testing
Unit · controller slices
Integration with real containers
Full flow, end to end
Operations
Message queues · asynchronous work
Roles · auditing
Decisions written down
Experience file · piece 06 of 06Next step
06The kind of work I'm ready for
This job taught me to build the backend of something real: APIs another team consumes, rules that hold on the server, integrations that fail without taking the rest down, deploys, and the internal tools that keep the operation running.
APIs and contractsREST APIs and OpenAPI contracts a frontend can consume without asking.
Modules with rulesBusiness logic validated on the server, with states modelled properly.
IntegrationsExternal services behind an interface, so swapping one isn't a rewrite.
Data and filesRelational databases, versioned migrations, and object storage.
DeploymentDocker, infrastructure as code, and configuration that lives in the environment.
Writing it downTechnical decisions documented, and work shared with frontend and product.