Skip to content

Commit e08233a

Browse files
authored
Merge pull request #292 from finos/feat/architecture-cleanup
refactor: architecture remediation — dead code, layering, registry, concurrency, god-object decomposition
2 parents 7bf370c + db3a915 commit e08233a

263 files changed

Lines changed: 11099 additions & 9449 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.importlinter

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
[importlinter]
2+
root_package = orb
3+
include_external_packages = True
4+
5+
# ---------------------------------------------------------------------------
6+
# Contract 1: domain-is-clean
7+
#
8+
# The domain layer must not import from any outer layer or from CLI/UI
9+
# frameworks (argparse, etc.). This is the strictest contract — any new
10+
# violation here is a clean-architecture regression.
11+
#
12+
# NOTE: allow_indirect_imports = True is required because import-linter
13+
# resolves the root `orb` package as an intermediate node when any domain
14+
# module does `from orb.domain...`. The root orb/__init__.py re-exports
15+
# SDK/bootstrap symbols which transitively touch outer layers, creating
16+
# false positives on every domain file. Direct-only enforcement is the
17+
# correct policy here.
18+
# ---------------------------------------------------------------------------
19+
[importlinter:contract:domain-is-clean]
20+
name = Domain layer is clean (no outer-layer imports)
21+
type = forbidden
22+
source_modules = orb.domain
23+
forbidden_modules =
24+
orb.application
25+
orb.infrastructure
26+
orb.interface
27+
orb.providers
28+
orb.monitoring
29+
orb.cli
30+
argparse
31+
allow_indirect_imports = True
32+
# No baseline ignore_imports needed: the only domain argparse violation
33+
# (provider_cli_spec_port) was moved to orb.providers.base in this branch (E2).
34+
# Any future argparse-in-domain import is now a real CI failure.
35+
36+
37+
# ---------------------------------------------------------------------------
38+
# Contract 2: application-no-infra-concretes
39+
#
40+
# The application layer must not import from infrastructure concretes,
41+
# provider implementations, or monitoring adapters. It may only depend on
42+
# domain (pure) and its own cross-cutting application interfaces.
43+
#
44+
# All A1/A2/A3 baseline violations have been fixed by Epic E4 / ticket #1358:
45+
# A1 — TemplateDTO moved to orb.application.dto.template
46+
# A2 — same fix as A1
47+
# A3 — MetricsCollector now injected via MetricsPort
48+
# ---------------------------------------------------------------------------
49+
[importlinter:contract:application-no-infra-concretes]
50+
name = Application layer does not import infrastructure concretes
51+
type = forbidden
52+
source_modules = orb.application
53+
forbidden_modules =
54+
orb.infrastructure
55+
orb.providers
56+
orb.monitoring
57+
allow_indirect_imports = True
58+
59+
60+
# ---------------------------------------------------------------------------
61+
# Contract 3: infrastructure-no-upward
62+
#
63+
# The infrastructure layer must not import from the interface/CLI layers.
64+
# It adapts domain ports to external systems; it must not depend on the
65+
# presentation/CLI tier.
66+
#
67+
# I1 baseline violation has been fixed by Epic E4 / ticket #1358:
68+
# I1 — console_adapter inverted: it now owns Rich logic and cli/console.py
69+
# delegates downward to the adapter (cli → infrastructure, correct).
70+
# ---------------------------------------------------------------------------
71+
[importlinter:contract:infrastructure-no-upward]
72+
name = Infrastructure layer does not import upward layers
73+
type = forbidden
74+
source_modules = orb.infrastructure
75+
forbidden_modules =
76+
orb.interface
77+
orb.cli
78+
allow_indirect_imports = True

docs/root/architecture/machine-discovery-flow.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ sequenceDiagram
4444
sequenceDiagram
4545
participant HF as HostFactory
4646
participant CLI as CLI/API
47-
participant QRY as GetRequestHandler
47+
participant QRY as SyncAndGetRequestHandler
4848
participant DB as Storage
4949
participant AWS as AWS Provider
5050
participant HAND as AWS Handler
5151
5252
HF->>CLI: getRequestStatus.sh req-12345
53-
CLI->>QRY: GetRequestQuery
53+
CLI->>QRY: SyncAndGetRequestQuery
5454
QRY->>DB: Find request by ID
5555
DB-->>QRY: Request (with resource_ids + provider_api)
5656
QRY->>DB: Find machines by request_id

docs/root/developer_guide/adding_a_provider.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Complete these steps in order. Each one has a corresponding section in the exten
5353

5454
### 1. Create the provider package
5555

56-
Create `src/orb/providers/<name>/` following the layout above. The strategy class must extend `BaseProviderStrategy` from `orb.providers.base.strategy.base_provider_strategy`.
56+
Create `src/orb/providers/<name>/` following the layout above. The strategy class must extend `ProviderStrategy` from `orb.providers.base.strategy` (see `src/orb/providers/base/strategy/provider_strategy.py`). Both the AWS and k8s providers use this base — for example, `AWSProviderStrategy(ProviderStrategy)` in `src/orb/providers/aws/strategy/aws_provider_strategy.py`.
5757

5858
### 2. Add an enum entry
5959

@@ -579,6 +579,6 @@ The AWS provider tests are the reference layout:
579579
## Cross-references
580580

581581
- [Clean Architecture](../architecture/clean_architecture.md) — layer boundaries enforced by architecture tests
582-
- [Strategy Pattern](../patterns/strategy_pattern.md) — how `BaseProviderStrategy` and `ProviderRegistry` work together
582+
- [Strategy Pattern](../patterns/strategy_pattern.md) — how `ProviderStrategy` and `ProviderRegistry` work together
583583
- [Ports and Adapters](../patterns/ports_and_adapters.md) — the port/registry decoupling pattern used throughout
584584
- AWS reference implementation: `src/orb/providers/aws/registration.py`

docs/root/developer_guide/cqrs.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ Queries represent read operations that don't change system state:
245245

246246
```python
247247
@dataclass(frozen=True)
248-
class GetRequestQuery:
249-
"""Query to get a specific request."""
248+
class SyncAndGetRequestQuery:
249+
"""Query to sync provider state and get a specific request."""
250250
request_id: str
251251

252252
def validate(self) -> None:
@@ -283,14 +283,14 @@ class GetRequestStatusQuery:
283283
Query handlers retrieve and format data for read operations:
284284

285285
```python
286-
class GetRequestQueryHandler:
287-
"""Handles request retrieval queries."""
286+
class SyncAndGetRequestHandler:
287+
"""Handles sync-and-get request queries."""
288288

289289
def __init__(self, request_read_model: RequestReadModel):
290290
self._read_model = request_read_model
291291
self._logger = get_logger(__name__)
292292

293-
async def handle(self, query: GetRequestQuery) -> Optional[RequestDto]:
293+
async def handle(self, query: SyncAndGetRequestQuery) -> Optional[RequestDto]:
294294
"""Handle the get request query."""
295295
try:
296296
query.validate()
@@ -478,8 +478,8 @@ def configure_cqrs(container: DIContainer) -> Tuple[CommandBus, QueryBus]:
478478

479479
# Register query handlers
480480
query_bus.register_handler(
481-
GetRequestQuery,
482-
GetRequestQueryHandler(request_read_model)
481+
SyncAndGetRequestQuery,
482+
SyncAndGetRequestHandler(request_read_model)
483483
)
484484

485485
return command_bus, query_bus
@@ -505,7 +505,7 @@ print(f"Created request: {request_id}")
505505

506506
```python
507507
# Get a specific request
508-
query = GetRequestQuery(request_id="req-123")
508+
query = SyncAndGetRequestQuery(request_id="req-123")
509509
request = await query_bus.dispatch(query)
510510

511511
if request:

docs/root/developer_guide/dependency_injection.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ class MigrateProviderConfigHandler:
167167
#### Query Handlers (Actual Examples)
168168

169169
```python
170-
# From src/application/queries/handlers.py
170+
# From src/application/queries/request_query_handlers.py
171171
@injectable
172-
class GetRequestHandler:
172+
class SyncAndGetRequestHandler:
173173
def handle(self, query):
174-
# Handle request retrieval
174+
# Handle sync-and-get request retrieval
175175
pass
176176

177177
# From src/application/queries/system_handlers.py
@@ -206,7 +206,7 @@ class UpdateRequestStatusCommand(Command, BaseModel):
206206
# ... other fields
207207

208208
# From src/application/dto/queries.py
209-
class GetRequestQuery(Query, BaseModel):
209+
class SyncAndGetRequestQuery(Query, BaseModel):
210210
request_id: str
211211
# ... other fields
212212

@@ -286,7 +286,7 @@ Based on actual codebase analysis:
286286
- `MigrateProviderConfigHandler`
287287
- And many more...
288288
- Query handlers in `src/application/queries/`:
289-
- `GetRequestHandler`
289+
- `SyncAndGetRequestHandler`
290290
- `GetProviderConfigHandler`
291291
- `GetActiveMachineCountHandler`
292292
- And many more...

docs/root/sdk/api-reference.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Create a return request for one or more machines.
9898

9999
Retrieve the current status of a provisioning or return request.
100100

101-
**CQRS query:** `GetRequestQuery`
101+
**CQRS query:** `SyncAndGetRequestQuery`
102102

103103
| Interface | Parameter | CQRS field | Notes |
104104
|-----------|-----------|------------|-------|
@@ -145,7 +145,7 @@ Note: the REST handler uses `ListRequestsQuery` (from `orb.application.request.q
145145

146146
List requests pending machine return.
147147

148-
**CQRS query:** `ListReturnRequestsQuery`
148+
**CQRS query:** `SyncAndListReturnRequestsQuery`
149149

150150
| Interface | Parameter | CQRS field | Notes |
151151
|-----------|-----------|------------|-------|
@@ -397,9 +397,9 @@ The SDK auto-discovers CQRS handlers and derives method names by stripping the `
397397
| `CreateTemplateCommand` | `create_template` |
398398
| `UpdateTemplateCommand` | `update_template` |
399399
| `DeleteTemplateCommand` | `delete_template` |
400-
| `GetRequestQuery` | `get_request` |
400+
| `SyncAndGetRequestQuery` | `get_request` |
401401
| `ListActiveRequestsQuery` | `list_active_requests` |
402-
| `ListReturnRequestsQuery` | `list_return_requests` |
402+
| `SyncAndListReturnRequestsQuery` | `list_return_requests` |
403403
| `GetTemplateQuery` | `get_template` |
404404
| `ListTemplatesQuery` | `list_templates` |
405405
| `ValidateTemplateQuery` | `validate_template` |

makefiles/ci.mk

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ ci-arch-file-sizes: ## Check file size compliance
4242
@echo "Running file size checks..."
4343
./dev-tools/quality/dev_tools_runner.py check-file-sizes --warn-only
4444

45+
ci-arch-lint-imports: dev-install ## Run import-linter layer-boundary contracts
46+
@echo "Running import-linter layer-boundary checks..."
47+
$(call run-tool,lint-imports,)
48+
4549
# Composite target
46-
ci-architecture: ci-arch-cqrs ci-arch-clean ci-arch-imports ci-arch-file-sizes ## Run all architecture checks
50+
ci-architecture: ci-arch-cqrs ci-arch-clean ci-arch-imports ci-arch-file-sizes ci-arch-lint-imports ## Run all architecture checks
4751

4852
# Individual security targets (with tool names)
4953
ci-security-bandit: ## Run Bandit security scan

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ ci = [
204204
"pyright>=1.1.408,<2.0.0",
205205
"bandit>=1.7.5,<2.0.0",
206206
"bandit-sarif-formatter>=1.1.1,<2.0.0",
207+
"import-linter>=2.0,<3.0.0",
207208
# Testing Framework
208209
"pytest>=7.4.3,<10.0.0",
209210
"pytest-cov>=4.1.0,<8.0.0",
@@ -293,6 +294,7 @@ ci = [
293294
"pyright>=1.1.408,<2.0.0",
294295
"bandit>=1.7.5,<2.0.0",
295296
"bandit-sarif-formatter>=1.1.1,<2.0.0",
297+
"import-linter>=2.0,<3.0.0", # Architecture layer-boundary enforcement
296298

297299
# Testing Framework
298300
"pytest>=7.4.3,<10.0.0",

src/orb/api/models/responses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pydantic import BaseModel, ConfigDict
88

99
from orb.application.request.dto import MachineReferenceDTO
10-
from orb.infrastructure.error.exception_handler import InfrastructureErrorResponse
10+
from orb.infrastructure.error.responses import InfrastructureErrorResponse
1111

1212
# ---------------------------------------------------------------------------
1313
# Pydantic response models (snake_case, default scheduler canonical format)

0 commit comments

Comments
 (0)