Detailed architecture documentation for the Bidder backend application.
The project follows a layered, domain-driven architecture:
bidder-application (entry point)
├── bidder-services (7 domain services, 14 modules)
│ ├── role-model-service-api/impl
│ ├── user-profile-service-api/impl
│ ├── organizations-service-api/impl
│ ├── projects-service-api/impl
│ ├── accounts-service-api/impl
│ ├── campaigns-service-api/impl
│ └── campaign-settings-service-api/impl
├── bidder-libraries (10 shared libraries)
│ ├── context, role-model, errors, util, validation
│ ├── object-mapper, cookies
│ └── state-machine, money, weekly-activity
├── bidder-clients (2 external API clients)
│ ├── keycloak
│ └── wildberries
├── bidder-migrations (Liquibase)
└── bidder-e2e-tests (integration tests)
@SpringBootApplication*-service-impl modulesEach service follows the API/Implementation pattern:
*-service-api: Interface definitions, DTOs, domain models*-service-impl: Controllers, DAOs, repositories, converters| Service | Purpose |
|---|---|
role-model | Core authorization (used by all services) |
user-profile | User management and profiles |
organizations | Multi-tenant organization management |
projects | Project management within organizations |
accounts | Wildberries account management |
campaigns | Campaign lifecycle management |
campaign-settings | Campaign configuration |
| Library | Purpose |
|---|---|
context | Caller context DSL for coroutines |
role-model | RBAC framework |
errors | Centralized error handling |
util | Common utilities |
state-machine | Type-safe state machine framework |
money | Type-safe money classes |
weekly-activity | Weekly schedule bitmap |
keycloak: Keycloak Admin API client (OpenAPI-generated)wildberries: Marketplace API client with endpoint registry patternCustom RBAC system separate from Keycloak roles.
Core Concepts:
Flow:
withCallerContext({ fromSecurityContext() })authorizationManager.authorize(action, resource)Example: See OrganizationsController.kt:51-58 and AuthorizedOrganizationService.kt
Services use DAO pattern with Spring Data R2DBC:
Entity → Repository → DAO Interface → DAO Implementation → Service
Flow<T> or suspended valuesconverter/ package*-service-impl/controller/@RestController with @RequestMapping("/api/v1/...")suspend funwithCallerContext({ fromSecurityContext() }) { } for auth contextResponseEntity<T> with .asResponseEntity()Organization (top-level tenant)
├── Projects (many-to-many via join table)
├── Accounts (many-to-many via join table)
└── Members (via ORGANIZATION_MEMBERSHIPS)
Project
├── belongs to Organization(s)
├── parent Project (self-referential)
└── Campaigns
Campaign
├── belongs to Project
├── uses Account
└── has Settings
All entities use soft delete:
deletedAt: LocalDateTime? column.filter { it.deletedAt == null }Dedicated service for campaign configuration, separate from lifecycle management.
Facade: CampaignSettingsFacade - unified settings operations
Domain Services:
| Service | Settings |
|---|---|
CampaignGeneralSettingsService | Pause/managed state |
CampaignBudgetSettingsService | Budget, regions |
CampaignScheduleSettingsService | Weekly schedule |
PlacementSettingsService | Zone-specific bids |
KeyPhraseService | Target/Plus/Minus phrases |
Endpoints:
GET /api/v1/campaign/settings - Get all settingsPOST /api/v1/campaign/settings/update - Update settingsSingle source of truth: WbEndpoints.kt
object WbEndpoints {
object Marketing {
val START_CAMPAIGN = Endpoint(HttpMethod.Get, "/adv/v0/start")
// ...
}
}
Used by both production clients and WireMock test stubs.
| Client | Purpose |
|---|---|
WbGeneralClient | Seller info, ping, token validation |
WbMarketingClient | Campaigns, budgets, bidding |
CoreException (base)
├── ValidationException (400) - with List<Violation>
└── Domain exceptions (various codes)
ErrorHandler converts exceptions to REST responses with localized messages.