Operational Admin Platform Modernization
33 domains, 170+ integrations, validation, authorization, and integration testing at enterprise scale
Data verified from git shortlog, Jira API, and codebase inspection
An internal admin platform supporting operations, compliance, merchant support, finance, and campaign teams — spanning 33 feature domains and consuming 170+ external services. I contributed to validation patterns, integration architecture, authorization, error handling, database migrations, and automated testing.
Platform Context
The platform had grown organically as the company scaled from startup to enterprise. What started as a single-team tool had become the operational backbone for cross-functional payment teams — but the architecture hadn't kept pace. Validation was inconsistent across controllers. Integration clients had duplicated configuration and no standard error handling. Authorization checks were scattered through service methods rather than centralized at the boundary. Testing coverage was uneven, making refactoring risky.
Validation at Scale
The first systematic improvement was bringing consistency to controller-level validation. Different controllers used different validation approaches — some used Bean Validation annotations, others had inline checks in service methods, and some had no validation at all. I designed a set of custom Bean Validation annotations that captured the most common validation patterns across the platform: boundary checks on numeric fields, cross-field comparisons, and conditional required fields based on domain state.
The key constraint was that adoption had to be incremental. Teams couldn't stop feature work to rewrite validation across 33 domains. Each annotation was introduced alongside existing validation in a controller, migrated over time, and eventually became the standard pattern. The compound interest of this approach meant that six months in, new controllers started with validation annotations by convention rather than by decision.
Integration Architecture
With 170+ external service integrations, consistency in how services were called — and how failures were handled — was critical. The existing pattern used raw OpenFeign clients with per-service configuration, but configuration conventions were maintained by tribal knowledge rather than code structure.
I standardized on a pattern: each external integration has a dedicated Feign client interface, a configuration class that defines the URL, timeouts, and error decoder, and a WireMock test that validates the integration behavior. The WireMock tests became especially valuable — they caught subtle serialization mismatches, unexpected response formats, and timeout configuration issues before they reached production.
1@FeignClient(name = "payment-service", configuration = PaymentServiceConfig.class)2public interface PaymentServiceClient {3@PostMapping("/api/v1/payments/validate")4ValidationResponse validate(@RequestBody PaymentRequest request);5}6 7@Configuration8public class PaymentServiceConfig {9@Bean10public RequestInterceptor authInterceptor() {11 return template -> template.header("Authorization", "Bearer " + tokenProvider.getToken());12}13 14@Bean15public ErrorDecoder errorDecoder() {16 return new PaymentServiceErrorDecoder();17}18}Authorization at the Boundary
Authorization logic had accumulated in service methods across the codebase, making it hard to audit and easy to miss. The principle was: authorization should happen at the boundary, not buried in the implementation. Every controller endpoint should reject unauthorized requests before any business logic executes.
The approach used a combination of OPA (Open Policy Agent) for policy evaluation, OAuth2 scopes for token-level permissions, and RBAC for user-role assignments. A custom Spring interceptor caught all incoming requests, extracted the principal from the JWT, evaluated the applicable policies, and rejected or allowed the request before it reached the controller.
Boundary Authorization over Inline Checks
Authorization checks scattered through service methods made it hard to audit who could do what. Different domains had different authorization patterns, and there was no single place to look for access control rules.
- Authorization is auditable from a single interceptor point
- Service methods no longer contain authorization logic
- New endpoints get authorization by default via annotation
- Interceptor-based auth is all-or-nothing — fine-grained per-endpoint rules add complexity
- OPA policy updates require deployment coordination across teams
Integration Testing with Playwright
The E2E testing suite uses Playwright to validate functionality, authorization, form submissions, file uploads, and multi-step workflows across the platform. With 300+ specs covering the most critical user journeys, the test suite serves as both regression protection and living documentation.
The testing strategy is layered: WireMock handles integration-level tests for individual service boundaries, Playwright covers end-to-end user journeys, and unit tests cover service and utility logic. Each layer catches different categories of issues — WireMock catches serialization and timeout problems, Playwright catches UI interaction and authorization regressions, and unit tests catch logic errors.
WireMock Integration Tests
~3 minValidate Feign client serialization, timeouts, and error handling against mock service boundaries.
Playwright E2E Suite
~12 minRun 300+ specs across admin workflows — login, CRUD, file uploads, multi-step forms, authorization checks.
Mutation Coverage Gate
~5 minEnsure new code maintains or improves mutation coverage thresholds before merge.
Review & Deploy
~30 minTeam review followed by automated deploy to staging, then production after smoke test validation.
Each pipeline stage catches a different kind of failure. A serialization mismatch in a Feign client is caught by WireMock before it reaches Playwright. A form validation regression is caught by Playwright before it reaches a code review. The pipeline pulls failures left, where they're cheaper and faster to fix.
Practical Lessons
A validation framework pays compound interest when it can be adopted incrementally. Teams don't need to rewrite everything at once — they introduce annotations alongside existing validation, and over time the new pattern becomes the default.
Integration testing at HTTP boundaries catches regressions that unit tests miss — especially serialization mismatches, response format changes, and timeout configuration errors. WireMock made these tests fast and reliable.
Long-lived admin platforms need continuous governance. Without ongoing attention to error codes, validation patterns, authorization rules, and package boundaries, a codebase degrades predictably. The most valuable contribution wasn't any single feature — it was establishing patterns that made the right thing the easy thing.
Flyway migrations need cross-team coordination. When multiple services share a MySQL instance, schema migrations can conflict. We adopted a convention: each service's tables are prefixed by domain namespace, and migration files include ownership metadata so teams know who to contact when a migration collides.
Confidentiality note: Case study details are anonymized. Metrics verified from git shortlog, Jira API (
assignee = currentUser()), and codebase inspection. No proprietary business logic, internal API contracts, or customer data are exposed.