| Preview #1 | Preview #2 | Preview #3 |
|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
.
├── doc
│ └── adr
│ └── 0001-project-initialization.md
│ └── ...
└── README.md
- 1. Project Initialization
- 2. Configure Java Version
- 3. Setup SpringBoot project structure
- 4. Healthcheck for Services
- 5. Swagger or OpenAPI rest api definition
- 6. Dockerize the Vehicle Service
- 7. Implement Insurance Service
- 8. Structured Logs
- 9. Distributed Tracing
- 10. Feature Toggle Integration
- 11. Green And Blue Deployment Strategy
- 12. Establish Quality Standards
- 13. Test Containers Setup
- 14. Github Action CI and CD
# install DIRENV (https://direnv.net/) and setup shell hook (https://direnv.net/docs/hook.html)
brew install direnv
# OR: curl -sfL https://direnv.net/install.sh | bash
# allow the project to be loaded
direnv allowAfter that start reading ADRs one-by-one to onboard and understand the project.
-
Architecture and Implementation
- simple project structure that allows to develop multiple microservices. Monorepo approach.
- Java 24, SpringBoot 3.5, Gradle KTS (Kotlin DSL)
- transparent dependencies management via TOML (
gradle/libs.versions.toml) - self-setup project, via DIRENV and BASH (Only DIRENV required for start working on project, everything else will be printed on terminal automatically)
- version control on project tools (e.g.
git,docker,java)
- version control on project tools (e.g.
- Dockerize the micro-services
- Activated Native Image optimization for micro-services (improve startup performance of the apps)
- [o] Prepare native image for Alpine Linux execution (MUSL library instead of glibc)
- Implemented structured logging for unified logs across micro-services (used Logdy, with potential to migrate to ELK stack or alternative)
- Implemented distributed tracing for unified traces across micro-services (used Jaeger). Observability.
- Implemented feature toggles (used Togglz)
- Implemented health checks for micro-services (with potential to apply metrics)
- Implemented Swagger for unified API documentation across micro-services
- Implemented Docker Compose for unified setup of micro-services (local dev environment)
- Non-bloacking Http clients (used Spring WebFlux)
- Basic Auth security (used Spring Security)
- Auto-generate service client from Swagger Definition (used OpenAPI Generator), Insurance --> Vehicle
- Swagger definitions can be use for simple contract tests (compare the snapshots of the API definitions)
- Schema Registry (AVRO, protobuffer, etc.)
- API versioning (or in api path, or by custom header attribute)
- Errors handling:
- Fallback to EMPTY data (prevent NULLs)
- Default SpringBoot error handling (e.g. 404)
- Exceptions raising, Custom error codes
- Fault tolerance:
- Circuit Breaker (Prevent cascading failures by short-circuiting calls to services that are failing or slow. Use libraries like Resilience4j)
- Bulkhead pattern (Isolate resources (threads, connections, memory) for different components or tenants to prevent one failing or slow service from consuming all resources. Achieve via separate thread pools or service instances.)
- Backpressure / Rate limiter (Limit the rate of requests to a service to prevent it from being overwhelmed. Use libraries like Resilience4j)
- Retry, Timeout (Set reasonable timeouts on service calls and retries with exponential backoff to avoid hanging and loading downstream services.)
- Saga pattern (https://microservices.io/patterns/data/saga.html)
- Event Driven Architecture (https://microservices.io/patterns/data/event-driven-architecture.html)
- CQRS (Command Query Responsibility Segregation), etc.
- Quota (limit the number of requests per tenant, Requests per minute, etc.)
- Best practices (performance, security, etc.)
- REST API special design for heavy operations, batches, jobs
- Streams support, deliver data in stream continuously to the client
-
Testing Approaches
- [o] Implemented several types of tests
- Test profile (spring-boot test profile with verbose logs)
- Unit tests (JUnit 5, Mockito)
- Integration tests (Spring Boot Test, RestTemplate)
- End-to-end tests (testcontainers)
- Performance/Load tests (Stubs, mockserver, OHA, WRK, K6, cadvisor)
- Contract tests (Pact, WireMock)
- [o] Implemented several types of tests
-
[o] Continues Delivery (DEFINED in ADR-0011)
- [o] Implemented CI/CD pipeline (Github actions)
- Based on running Gradle build tasks of different types (e.g.
build,test,integrationTest,dockerBuild,dockerPush). Build fails if one of the tasks fails. - Implemented Green/Blue deployment concept (used Github actions to demo the approach)
- Deployment versioning strategy (binaries versioning, docker tags, git tags, semver)
- Based on running Gradle build tasks of different types (e.g.
- Blue-Green deployment strategy description
- Rollback
- Scheduled delivery
- "Shadow modes" (Deploy new version alongside old, compare results of old and new implementation, switch to new version if results are better)
- Environments support (e.g. local dev, staging, production, performance, etc.)
- Use Terraform, LocalStack - for local dev environment setup that emulates AWS cloud environment
- [o] Implemented CI/CD pipeline (Github actions)
-
Documentation and Code Quality
- Used ADRs to document decisions on project (perfect for AI and human to understand project structure and decisions)
- Details are part of each ADR
- Quality Assurance (DEFINED in ADR-0012)
- Code coverage (JaCoCo)
- Static code analysis (Checkstyle, PMD)
- Code quality (SonarQube)
- Code style/formatting (Spotless)
- Git leaks (Gitleaks)
- Used ADRs to document decisions on project (perfect for AI and human to understand project structure and decisions)
- To accomplish all solution tasks will be required ~30-40 hours
| Phase | Estimated Time |
|---|---|
| Understanding & Planning | 2-3 hours |
| Architecture & Design | 3-4 hours |
| Setup & Initial Dev | 6-8 hours |
| Feature Toggling Setup | 3-4 hours |
| Testing Implementation | 5-6 hours |
| CI/CD Pipeline Setup | 3-4 hours |
| Documentation & Reflection | 2-3 hours |
| Bonus & Cleanup | 2-4 hours |
- more and more features enabled makes a project dependencies more complex and bloated.
As example: multiple redis client dependencies involved into project, but do we actually need this? By unified Redis client we can reduce the number of the dependencies and make project attack landscape smaller.
- Not all solutions start to work immediately. Latest versions of each tool usually require some additional steps to make it work.
Example: Jaeger does not want to work gRPC out of the box. I have to switch solution to HTTP/protobuf port.
-
Cross Platform development will require additional steps to make it work well.
-
Engineering perfection in each task raise the time estimates a lot.
Just to implement CI/CD pipeline that covers all documentated cases will be required several days, due to low testability of the GitHub Actions on local environment (very time-consuming process).
- There are many other Alternatives in how to implement each step and task. Just by choosing alternative to SpringBoot framework (Quarkus, Micronaut, Vertx, Ktor, etc.) will completely different dependencies and setup.














