Skip to main content

Operational Admin Platform Modernization

33 domains, 170+ integrations, validation, authorization, and integration testing at enterprise scale

Role: Staff Software Engineer — Full Stack PlatformPeriod: Mar 2024 — Present8 min read
Java 21Spring BootVue 3PiniaOpenFeignMySQLRedisKafkaOPAPlaywrightWireMock
33
Feature domains
170+
External integrations
300+
E2E integration specs
1,200+
Platform commits

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.

Admin Platform mockup

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.

Java 21
Spring Boot
Vue 3
Frontend
MySQL + Flyway
Persistence
Playwright
E2E testing

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.

Feign client pattern
Java
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}
Representational — simplified from production codebase. Each integration has its own config class, client interface, and WireMock test.

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.

Engineering Decision

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.

Decision: A Spring interceptor evaluates authorization at the controller boundary before any business logic. OPA provides policy evaluation, OAuth2 scopes provide token-level permissions, and RBAC handles user-role assignments.
Consequences:
  • Authorization is auditable from a single interceptor point
  • Service methods no longer contain authorization logic
  • New endpoints get authorization by default via annotation
Tradeoffs:
  • 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 min

Validate Feign client serialization, timeouts, and error handling against mock service boundaries.

Playwright E2E Suite

~12 min

Run 300+ specs across admin workflows — login, CRUD, file uploads, multi-step forms, authorization checks.

Mutation Coverage Gate

~5 min

Ensure new code maintains or improves mutation coverage thresholds before merge.

Review & Deploy

~30 min

Team 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.