Skip to content

feat: Refactor server to hexagonal architecture and implement in-memory and PostgreSQL adapters#212

Open
Ngha-Boris wants to merge 6 commits into
mainfrom
refactor-server-to-hexagonal
Open

feat: Refactor server to hexagonal architecture and implement in-memory and PostgreSQL adapters#212
Ngha-Boris wants to merge 6 commits into
mainfrom
refactor-server-to-hexagonal

Conversation

@Ngha-Boris

@Ngha-Boris Ngha-Boris commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR refactors the codebase to follow a hexagonal (ports-and-adapters) architecture, clearly separating domain logic from infrastructure and enabling easier testing, multiple backend support, and future extensibility.


Directory Structure Changes

src/
├── domain/
│   └── mod.rs              # Core entities, value objects, domain invariants
├── application/
│   └── mod.rs              # Inbound use cases (PublishStatusList, UpdateStatuses, etc.)
├── ports/
│   └── mod.rs              # Outbound port trait definitions
├── adapters/
│   ├── mod.rs              # Feature-gated adapter exports
│   ├── postgres.rs         # SeaORM repository implementations
│   ├── memory.rs           # In-memory adapters for unit testing
│   ├── cache.rs            # Moka-based status list cache
│   ├── redis.rs            # Redis storage for cert-manager
│   ├── aws.rs              # AWS S3 and Secrets Manager
│   ├── certificate.rs      # ACME certificate provider adapter
│   ├── dns.rs              # Route53 and Pebble DNS providers
│   ├── secret.rs           # Storage-backed secret store
│   └── metrics.rs          # Metrics collector implementations
├── utils/state.rs          # Dependency injection root (AppState + build_state)
└── lib.rs                  # Updated module/feature flag configuration

Key Implementation Details

1. Domain Layer (No Infrastructure Dependencies)

  • StatusList, StatusEntry, Status, Issuer, Credential entities
  • Status list bitstring encoding/decoding logic
  • Zero dependencies on Axum, SeaORM, Redis, AWS SDKs

2. Ports as Boundaries

All outbound dependencies are now expressed through traits in src/ports/:

  • StatusListRepository / CredentialRepository
  • StatusListCache
  • CertificateProvider
  • SecretStore
  • DnsProvider
  • MetricsCollector

3. Dependency Injection

AppState now holds trait objects:

pub struct AppState {
    pub status_lists: Arc<dyn StatusListRepository>,
    pub credentials: Arc<dyn CredentialRepository>,
    pub status_list_cache: Arc<dyn StatusListCache>,
    pub certificate_provider: Arc<dyn CertificateProvider>,
    pub secret_store: Arc<dyn SecretStore>,
    pub dns_provider: Arc<dyn DnsProvider>,
    pub metrics_collector: Arc<dyn MetricsCollector>,
    // ... config fields
}

Adapters are constructed in build_state() and injected, not built inline in handlers.

4. Compile-Time Feature Flags

Feature Description
memory-only Infrastructure-free build (domain + in-memory adapters only)
postgres PostgreSQL/SeaORM repositories
redis Redis cache/storage
aws AWS S3, Secrets Manager, Route53
cache-moka In-process Moka cache
certificate-acme ACME certificate provisioning
metrics-prometheus Prometheus metrics collection

Testing Strategy

Unit Tests with In-Memory Adapters

The MemoryStatusLists, MemoryStatusListCache, MemoryCredentials, MemorySecretStore, MemoryDnsProvider, and MemoryMetricsCollector adapters enable pure unit testing without infrastructure:

#[tokio::test]
async fn application_services_work_without_infrastructure() {
    let repo = Arc::new(MemoryStatusLists::default());
    let cache = Arc::new(MemoryStatusListCache::default());
    PublishStatusList::new(repo.clone()).execute(record()).await.unwrap();
    // ...
}

Integration Tests

Existing integration tests continue to work with the default Postgres/Redis/AWS stack.


Documentation

  • Created docs/hexagonal-architecture.md explaining the architecture layers and dependency rule
  • Updated README.md with link to architecture documentation
  • Module-level documentation in src/domain/, src/application/, and src/ports/

Verification

Build Memory-Only Composition

cargo check --no-default-features --features memory-only

Compiles without Postgres, Redis, or AWS dependencies.

Run All Tests

cargo test
cargo nextest run --workspace --all-targets --all-features

Checklist (Ticket Deliverables)

  • Audit current directory structure and identify entities, repositories, external clients
  • Define domain module (src/domain) with core entities and value objects
  • Define inbound application services (src/application)
  • Define outbound port traits (src/ports)
  • Move existing implementations into adapter modules (src/adapters/*)
  • Refactor AppState / startup.rs for dependency injection
  • Keep existing behavior intact (no functional changes)
  • Compile-time feature flags per adapter
  • Unit tests using in-memory/mock adapters
  • Update architecture documentation (docs/hexagonal-architecture.md)
  • Update README references

Related Issues


Migration Notes for Developers

  1. New code should follow the dependency rule: domain → application → ports → adapters
  2. Testing can use the memory adapters for fast, isolated unit tests
  3. New adapters should implement the corresponding port trait and be feature-gated
  4. Dependency injection happens in utils/state.rs - add new services there

@Ngha-Boris
Ngha-Boris requested review from martcpp and removed request for ndefokou July 15, 2026 11:59

@Hermann-Core Hermann-Core left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ngha-Boris I have reviewed the PR and posted comments for your attention.

Comment thread src/domain/mod.rs Outdated
Comment thread src/ports/mod.rs Outdated
Comment thread src/utils/state.rs
@Ngha-Boris
Ngha-Boris requested a review from Hermann-Core July 15, 2026 15:34
@Ngha-Boris

Copy link
Copy Markdown
Collaborator Author

Hello @Hermann-Core please check again

@martcpp

martcpp commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

this pr should not be merge till Friday thanks

@Ngha-Boris

Copy link
Copy Markdown
Collaborator Author

this pr should not be merge till Friday thanks

What is the reason for that @martcpp?

@martcpp

martcpp commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

this pr should not be merge till Friday thanks

What is the reason for that @martcpp?

it structural revamp there are other ticket that should be cloud to avoid serious conflict

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor server to hexagonal architecture with domain models, ports, and dependency injection

3 participants