jbuild 0.1.8

High-performance Java build tool supporting Maven and Gradle
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
# DDD Migration Guide for jbuild

## Overview

This guide outlines the migration path from the current architecture to a full Domain-Driven Design (DDD) implementation. The migration is designed to be incremental, allowing the codebase to remain functional throughout the process.

## Current Status

✅ **Completed:**
- DDD architecture documentation created (`docs/DDD_ARCHITECTURE.md`)
- 10 bounded contexts identified and documented
- Domain layer structure created (`src/domain/`)
- Shared domain concepts implemented (value objects, domain events)
- Build System context with detector service
- Artifact context with value objects and repository traits
- ARCHITECTURE.md updated with DDD principles
- TODO.md updated with DDD adoption status

⏳ **In Progress:**
- Refactoring existing code to domain layer
- Repository implementations
- Domain events publishing and handling

## Migration Strategy

### Phase 1: Foundation (Completed ✅)

1. **Create Domain Layer Structure**
   - ✅ Created `src/domain/` module
   - ✅ Defined 10 bounded contexts
   - ✅ Created module structure for each context

2. **Define Shared Concepts**
   - ✅ Value objects: `Version`, `FilePath`, `JavaVersion`
   - ✅ Domain events: Base trait and common events
   - ✅ Shared utilities

3. **Documentation**
   - ✅ Comprehensive DDD architecture guide
   - ✅ Updated main ARCHITECTURE.md
   - ✅ Migration guide (this document)

### Phase 2: Value Objects Extraction (Next)

**Goal:** Replace primitive types with rich value objects

**Tasks:**
1. **Artifact Coordinates**
   - Refactor `artifact::ArtifactCoordinates` to use `domain::artifact::value_objects::ArtifactCoordinates`
   - Update all usages across the codebase
   - Add validation logic to value object

2. **Version Handling**
   - Replace string versions with `domain::shared::Version`
   - Implement semantic version comparison
   - Add snapshot detection logic

3. **Scope and Lifecycle**
   - Extract `Scope` enum to `domain::artifact::value_objects::Scope`
   - Extract `LifecyclePhase` to `domain::maven::value_objects::LifecyclePhase`
   - Add domain logic to these types

**Example Refactoring:**

Before:
```rust
pub struct Dependency {
    pub group_id: String,
    pub artifact_id: String,
    pub version: String,
    pub scope: Option<String>,
}
```

After:
```rust
use crate::domain::artifact::value_objects::{ArtifactCoordinates, Scope};
use crate::domain::shared::Version;

pub struct Dependency {
    coordinates: ArtifactCoordinates,
    scope: Scope,
}

impl Dependency {
    pub fn new(coordinates: ArtifactCoordinates, scope: Scope) -> Self {
        Self { coordinates, scope }
    }
    
    pub fn coordinates(&self) -> &ArtifactCoordinates {
        &self.coordinates
    }
    
    pub fn scope(&self) -> Scope {
        self.scope
    }
}
```

### Phase 3: Aggregate Roots (After Phase 2)

**Goal:** Define aggregate boundaries and roots

**Tasks:**
1. **MavenProject Aggregate**
   - Define `domain::maven::aggregates::MavenProject`
   - Encapsulate Model, Dependencies, Plugins
   - Enforce invariants (valid GAV, valid phases)
   - Migrate from `core::MavenProject`

2. **GradleProject Aggregate**
   - Define `domain::gradle::aggregates::GradleProject`
   - Encapsulate Tasks, Configurations, SourceSets
   - Enforce invariants (no circular task dependencies)
   - Migrate from `gradle::GradleProject`

3. **Artifact Aggregate**
   - Define `domain::artifact::aggregates::Artifact`
   - Encapsulate coordinates and file path
   - Enforce invariants (valid coordinates, file exists)

**Aggregate Design Pattern:**
```rust
pub struct MavenProject {
    // Aggregate root
    coordinates: ArtifactCoordinates,
    model: Model,
    dependencies: Vec<Dependency>,
    plugins: Vec<Plugin>,
}

impl MavenProject {
    // Factory method with validation
    pub fn new(coordinates: ArtifactCoordinates, model: Model) -> Result<Self> {
        // Validate invariants
        if !coordinates.is_valid() {
            return Err(anyhow!("Invalid coordinates"));
        }
        
        Ok(Self {
            coordinates,
            model,
            dependencies: Vec::new(),
            plugins: Vec::new(),
        })
    }
    
    // Domain methods
    pub fn add_dependency(&mut self, dep: Dependency) -> Result<()> {
        // Business logic
        self.dependencies.push(dep);
        Ok(())
    }
    
    // No direct access to internals
    pub fn coordinates(&self) -> &ArtifactCoordinates {
        &self.coordinates
    }
}
```

### Phase 4: Domain Services (After Phase 3)

**Goal:** Extract business logic into domain services

**Tasks:**
1. **Dependency Resolution Service**
   - Move logic from `resolver/` to `domain::artifact::services`
   - Implement `DependencyResolver` domain service
   - Use repository traits for data access

2. **Model Building Service**
   - Move logic from `model/model_builder.rs` to `domain::maven::services`
   - Implement `ModelBuilder` domain service
   - Handle POM inheritance and interpolation

3. **Task Graph Service**
   - Move logic from `gradle/task_graph.rs` to `domain::gradle::services`
   - Implement `TaskGraphBuilder` domain service
   - Handle task dependencies and cycles

**Domain Service Pattern:**
```rust
pub struct DependencyResolver {
    local_repo: Arc<dyn ArtifactRepository>,
    remote_repos: Vec<RemoteRepository>,
}

impl DependencyResolver {
    pub fn new(
        local_repo: Arc<dyn ArtifactRepository>,
        remote_repos: Vec<RemoteRepository>,
    ) -> Self {
        Self { local_repo, remote_repos }
    }
    
    pub fn resolve(&self, coords: &ArtifactCoordinates) -> Result<Artifact> {
        // Domain logic for resolution
        if let Some(path) = self.local_repo.find(coords)? {
            return Ok(Artifact::new(coords.clone(), path));
        }
        
        // Try remote repositories
        for repo in &self.remote_repos {
            if let Some(artifact) = repo.download(coords)? {
                self.local_repo.install(coords, artifact.path())?;
                return Ok(artifact);
            }
        }
        
        Err(anyhow!("Artifact not found: {}", coords))
    }
}
```

### Phase 5: Repository Pattern (After Phase 4)

**Goal:** Implement repository traits for all data access

**Tasks:**
1. **Artifact Repository**
   - Implement `domain::artifact::repositories::ArtifactRepository` trait
   - Create `LocalArtifactRepository` implementation
   - Migrate from `artifact::LocalRepository`

2. **Model Repository**
   - Create `ModelRepository` trait for POM access
   - Implement file-based and remote implementations

3. **Configuration Repository**
   - Create repository for jbuild.toml access
   - Implement workspace configuration loading

**Repository Implementation:**
```rust
pub struct LocalArtifactRepository {
    base_path: PathBuf,
}

impl ArtifactRepository for LocalArtifactRepository {
    fn find(&self, coords: &ArtifactCoordinates) -> Result<Option<PathBuf>> {
        let path = self.base_path
            .join(coords.group_id().replace('.', "/"))
            .join(coords.artifact_id())
            .join(coords.version())
            .join(format!("{}-{}.{}", 
                coords.artifact_id(), 
                coords.version(), 
                coords.extension()));
        
        if path.exists() {
            Ok(Some(path))
        } else {
            Ok(None)
        }
    }
    
    fn install(&self, coords: &ArtifactCoordinates, file: PathBuf) -> Result<()> {
        // Installation logic
        Ok(())
    }
    
    fn exists(&self, coords: &ArtifactCoordinates) -> bool {
        self.find(coords).ok().flatten().is_some()
    }
    
    fn path(&self) -> &PathBuf {
        &self.base_path
    }
}
```

### Phase 6: Domain Events (After Phase 5)

**Goal:** Implement event-driven communication between contexts

**Tasks:**
1. **Event Publisher**
   - Create event bus/publisher
   - Implement async event handling
   - Add event persistence (optional)

2. **Event Handlers**
   - Create handlers for cross-context communication
   - Implement event-driven workflows
   - Add event logging

3. **Key Events**
   - `ProjectBuiltEvent` - triggers packaging
   - `DependencyResolvedEvent` - triggers compilation
   - `CompilationCompletedEvent` - triggers testing
   - `TestSuiteCompletedEvent` - triggers reporting

**Event Pattern:**
```rust
pub struct EventBus {
    handlers: HashMap<String, Vec<Box<dyn EventHandler>>>,
}

impl EventBus {
    pub fn publish<E: DomainEvent>(&self, event: E) {
        if let Some(handlers) = self.handlers.get(event.event_type()) {
            for handler in handlers {
                handler.handle(&event);
            }
        }
    }
    
    pub fn subscribe<E: DomainEvent, H: EventHandler>(&mut self, handler: H) {
        self.handlers
            .entry(E::event_type())
            .or_insert_with(Vec::new)
            .push(Box::new(handler));
    }
}
```

### Phase 7: Layered Architecture (Final)

**Goal:** Complete separation into layers

**Tasks:**
1. **Presentation Layer**
   - Keep in `cli.rs` and `ui/`
   - Only handle user interaction
   - Delegate to application layer

2. **Application Layer**
   - Refactor `runner/` as application services
   - Orchestrate domain services
   - Handle transactions

3. **Domain Layer**
   - Complete migration to `domain/`
   - Pure business logic
   - No infrastructure dependencies

4. **Infrastructure Layer**
   - Keep in `artifact/`, `resolver/`, etc.
   - Implement repository traits
   - Handle external systems

## Testing Strategy

### Unit Tests
- Test value objects in isolation
- Test domain services with mock repositories
- Test aggregate invariants

### Integration Tests
- Test repository implementations
- Test service orchestration
- Test event handling

### Migration Tests
- Keep existing tests passing
- Add new tests for domain layer
- Gradually migrate tests to use domain types

## Benefits After Migration

1. **Maintainability**
   - Clear separation of concerns
   - Easy to understand domain logic
   - Reduced coupling between modules

2. **Testability**
   - Mock-friendly interfaces
   - Isolated domain logic
   - Comprehensive test coverage

3. **Extensibility**
   - Easy to add new build systems
   - Plugin architecture
   - Event-driven workflows

4. **Team Alignment**
   - Ubiquitous language
   - Clear domain boundaries
   - Consistent terminology

## Migration Checklist

- [x] Phase 1: Foundation
  - [x] Create domain layer structure
  - [x] Define shared concepts
  - [x] Write documentation

- [ ] Phase 2: Value Objects Extraction
  - [ ] Extract ArtifactCoordinates
  - [ ] Extract Version
  - [ ] Extract Scope and LifecyclePhase
  - [ ] Update all usages

- [ ] Phase 3: Aggregate Roots
  - [ ] Define MavenProject aggregate
  - [ ] Define GradleProject aggregate
  - [ ] Define Artifact aggregate
  - [ ] Migrate existing code

- [ ] Phase 4: Domain Services
  - [ ] Implement DependencyResolver
  - [ ] Implement ModelBuilder
  - [ ] Implement TaskGraphBuilder
  - [ ] Migrate business logic

- [ ] Phase 5: Repository Pattern
  - [ ] Implement ArtifactRepository
  - [ ] Implement ModelRepository
  - [ ] Implement ConfigRepository
  - [ ] Migrate data access

- [ ] Phase 6: Domain Events
  - [ ] Create event bus
  - [ ] Implement event handlers
  - [ ] Add key events
  - [ ] Test event workflows

- [ ] Phase 7: Layered Architecture
  - [ ] Finalize presentation layer
  - [ ] Finalize application layer
  - [ ] Finalize domain layer
  - [ ] Finalize infrastructure layer

## Next Steps

1. Start Phase 2: Extract value objects
2. Update tests to use new value objects
3. Ensure all builds pass
4. Continue with Phase 3

## References

- [DDD_ARCHITECTURE.md]DDD_ARCHITECTURE.md - Detailed DDD documentation
- [ARCHITECTURE.md]../ARCHITECTURE.md - Overall architecture
- [TODO.md]../TODO.md - Current work items