qleany 1.7.3

Architecture generator for Rust and C++/Qt applications.
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
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# API Reference - Rust

This document is the API reference for Qleany-generated Rust code. It covers the APIs you interact with as a developer: **Entity Controllers**, **Feature Controllers**, and the **Unit of Work proc macros** you adapt when implementing custom use cases.

For general architecture and code structure, see [Generated Code - Rust](generated-code-rust.md).

---

## Entity Controller

**File:** `crates/direct_access/src/{entity}/{entity}_controller.rs`

Entity controllers are free functions (not methods on a struct) that provide CRUD and relationship operations on a single entity type. All operations are **synchronous** and return `anyhow::Result<T>`.

### CRUD Functions

#### create_orphan

```rust
pub fn create_orphan(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &CreateCarDto,
) -> Result<CarDto>
```

Creates a single entity without an owner.

#### create_orphan_multi

```rust
pub fn create_orphan_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[CreateCarDto],
) -> Result<Vec<CarDto>>
```

Batch version of `create_orphan`.

#### create

```rust
// Only available if the entity has an owner (defined in the manifest)
pub fn create(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &CreateCarDto,
    owner_id: EntityId,
    index: i32,              // insertion position; -1 = append
) -> Result<CarDto>
```

Creates an entity and attaches it to its owner.

#### create_multi

```rust
pub fn create_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[CreateCarDto],
    owner_id: EntityId,
    index: i32,
) -> Result<Vec<CarDto>>
```

Batch version of `create`.

#### get

```rust
pub fn get(
    db_context: &DbContext,
    id: &EntityId,
) -> Result<Option<CarDto>>
```

Fetches a single entity by ID. Returns `None` if not found.

#### get_multi

```rust
pub fn get_multi(
    db_context: &DbContext,
    ids: &[EntityId],
) -> Result<Vec<Option<CarDto>>>
```

Fetches multiple entities. Each entry is `None` if the corresponding ID was not found.

#### get_all

```rust
pub fn get_all(
    db_context: &DbContext,
) -> Result<Vec<CarDto>>
```

Returns all entities of this type. Use with caution on large tables.

#### update

```rust
pub fn update(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &UpdateCarDto,
) -> Result<CarDto>
```

Updates scalar fields only (no relationship changes). Accepts `UpdateCarDto` which contains `id` + scalar fields. Convert from `CarDto` via `.into()`:

```rust
let dto: CarDto = car_controller::get(&db, &id)?.unwrap();
let update_dto: UpdateCarDto = dto.into(); // drops relationship fields
```

#### update_multi

```rust
pub fn update_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[UpdateCarDto],
) -> Result<Vec<CarDto>>
```

Batch version of `update`.

#### update_with_relationships

```rust
pub fn update_with_relationships(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &CarDto,
) -> Result<CarDto>
```

Updates both scalar fields and relationship (junction table) data. Accepts the full `CarDto`. Use when you need to change relationship fields alongside scalar fields.

#### update_with_relationships_multi

```rust
pub fn update_with_relationships_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[CarDto],
) -> Result<Vec<CarDto>>
```

Batch version of `update_with_relationships`.

#### remove

```rust
pub fn remove(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    id: &EntityId,
) -> Result<()>
```

Deletes a single entity. Strong (owned) children are cascade-deleted.

#### remove_multi

```rust
pub fn remove_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    ids: &[EntityId],
) -> Result<()>
```

Batch version of `remove`.

### Relationship Functions

Only available if the entity has forward relationships defined in the manifest.

#### get_relationship

```rust
pub fn get_relationship(
    db_context: &DbContext,
    id: &EntityId,
    field: &CarRelationshipField,
) -> Result<Vec<EntityId>>
```

Returns the IDs of related entities for a given relationship field.

#### get_relationship_many

```rust
pub fn get_relationship_many(
    db_context: &DbContext,
    ids: &[EntityId],
    field: &CarRelationshipField,
) -> Result<HashMap<EntityId, Vec<EntityId>>>
```

Batch version of `get_relationship`. Returns a map from each entity ID to its related IDs.

#### get_relationship_count

```rust
pub fn get_relationship_count(
    db_context: &DbContext,
    id: &EntityId,
    field: &CarRelationshipField,
) -> Result<usize>
```

Returns the number of related entities without loading them.

#### get_relationship_in_range

```rust
pub fn get_relationship_in_range(
    db_context: &DbContext,
    id: &EntityId,
    field: &CarRelationshipField,
    offset: usize,
    limit: usize,
) -> Result<Vec<EntityId>>
```

Returns a paginated slice of related entity IDs, starting at `offset` and returning at most `limit` entries.

#### set_relationship

```rust
pub fn set_relationship(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    dto: &CarRelationshipDto,
) -> Result<()>
```

Replaces the relationship. The `CarRelationshipDto` contains the entity ID, the relationship field, and the new list of related IDs.

#### move_relationship

```rust
pub fn move_relationship(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    // only if entity is undoable:
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    id: &EntityId,
    field: &CarRelationshipField,
    ids_to_move: &[EntityId],
    new_index: i32,
) -> Result<Vec<EntityId>>
```

Reorders specific related IDs within an ordered relationship. Takes the entity ID, the relationship field, the IDs to move, and the new index (`-1` means append at end). Returns the reordered list of related IDs.

### Usage Examples

```rust
use direct_access::car::controller;
use direct_access::car::dtos::{CreateCarDto, CarDto};
use common::types::EntityId;

// Read-only operations (no event_hub needed)
let car = controller::get(&db_context, &EntityId::new(1))?;
let all_cars = controller::get_all(&db_context)?;
let some_cars = controller::get_multi(&db_context, &[EntityId::new(1), EntityId::new(2)])?;

// Write operations (need event_hub for event emission)
let created = controller::create_orphan(&db_context, &event_hub, &create_dto)?;
let updated = controller::update(&db_context, &event_hub, &car_dto)?;
controller::remove(&db_context, &event_hub, &EntityId::new(1))?;

// Undoable write operations (need undo_redo_manager)
let created = controller::create_orphan(
    &db_context, &event_hub, &mut undo_redo_manager, Some(stack_id), &create_dto,
)?;

// Relationships
let passenger_ids = controller::get_relationship(
    &db_context, &EntityId::new(1), &CarRelationshipField::Passengers,
)?;
let many = controller::get_relationship_many(
    &db_context, &[EntityId::new(1), EntityId::new(2)], &CarRelationshipField::Passengers,
)?;
let count = controller::get_relationship_count(
    &db_context, &EntityId::new(1), &CarRelationshipField::Passengers,
)?;
let page = controller::get_relationship_in_range(
    &db_context, &EntityId::new(1), &CarRelationshipField::Passengers, 0, 10,
)?;
controller::set_relationship(
    &db_context, &event_hub, &relationship_dto,
)?;
let reordered = controller::move_relationship(
    &db_context, &event_hub, &EntityId::new(1),
    &CarRelationshipField::Passengers, &[EntityId::new(3), EntityId::new(5)], -1,
)?;
```

---

## Feature Controller

**File:** `crates/{feature}/src/{feature}_controller.rs`

Feature controllers are free functions for custom use cases grouped by feature. The controller is generated; **you implement the use case logic**.

### Generated Functions

For each use case defined in the manifest, the controller generates a function. The shape depends on the use case configuration:

#### Standard use case (with input DTO, with output DTO)

```rust
pub fn save(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    dto: &SaveDto,
) -> Result<SaveResultDto>
```

#### Standard use case (no input DTO, no output DTO)

```rust
pub fn initialize(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
) -> Result<()>
```

#### Long operation use case

Long operations run on a background thread with progress tracking:

```rust
// Start the operation, returns an operation ID
pub fn generate_code(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    long_operation_manager: &mut LongOperationManager,
    dto: &GenerateCodeDto,
) -> Result<String>

// Poll progress
pub fn get_generate_code_progress(
    long_operation_manager: &LongOperationManager,
    operation_id: &str,
) -> Option<OperationProgress>

// Get result
pub fn get_generate_code_result(
    long_operation_manager: &LongOperationManager,
    operation_id: &str,
) -> Result<Option<GenerateCodeResultDto>>
```

### Event Emission

After a successful use case execution, the use case emits a feature event via the UoW's `publish_*_event()` method. This is called from within the use case, after the transaction commits:

```rust
// In the use case's execute() method, after commit:
uow.publish_save_event(vec![], None);
```

The UoW implementation sends the event directly through the `EventHub`:

```rust
// Generated implementation in the UoW:
fn publish_save_event(&self, ids: Vec<EntityId>, data: Option<String>) {
    self.event_hub.send_event(Event {
        origin: Origin::HandlingManifest(Save),
        ids,
        data,
    });
}
```

The `ids` and `data` parameters let you attach context to the event (e.g., which entity IDs were affected). The generated scaffold passes `vec![]` and `None` by default — adapt these in your use case implementation.

You can subscribe to these events to trigger UI updates or other reactions.

### Usage Examples

```rust
use handling_manifest::controller;

// Standard use case
let result = controller::save(&db_context, &event_hub, &save_dto)?;

// Long operation
let op_id = controller::generate_code(
    &db_context, &event_hub, &mut long_op_manager, &dto,
)?;

// Check progress (e.g., in a loop or callback)
if let Some(progress) = controller::get_generate_code_progress(&long_op_manager, &op_id) {
    println!("{}% - {}", progress.percentage, progress.message.unwrap_or_default());
}

// Get result when done
if let Some(result) = controller::get_generate_code_result(&long_op_manager, &op_id)? {
    println!("Generated {} files", result.file_count);
}
```

---

## Custom Unit of Work (Proc Macros)

**Files you edit:**

- Use case + trait: `crates/{feature}/src/use_cases/{use_case}_uc.rs`
- Implementation: `crates/{feature}/src/units_of_work/{use_case}_uow.rs`

When Qleany generates a custom feature use case, it scaffolds a UoW trait and implementation with `TODO` comments. Your job is to **adapt the `#[macros::uow_action]` attributes** to expose only the entity operations your use case needs.

### How It Works

The `#[macros::uow_action]` proc macro decorates the `impl Trait for UoW` block. Each attribute generates a trait method (on the trait) or an implementation (on the impl block). The same set of attributes must appear on **both** the trait definition and the impl block.

The generated UoW implements either `CommandUnitOfWork` (read-write) or `QueryUnitOfWork` (read-only) for transaction management.

### Available Actions

**Read-write actions** (use with `CommandUnitOfWork`):

| Action                         | Generated method signature                                                        |
|--------------------------------|-----------------------------------------------------------------------------------|
| `CreateOrphan`                 | `fn create_orphan_name(&self, entity: &Name) -> Result<Name>`                    |
| `CreateOrphanMulti`            | `fn create_orphan_name_multi(&self, entities: &[Name]) -> Result<Vec<Name>>`     |
| `Create`                       | `fn create_name(&self, entity: &Name, owner_id: EntityId, index: i32) -> Result<Name>` |
| `CreateMulti`                  | `fn create_name_multi(&self, entities: &[Name], owner_id: EntityId, index: i32) -> Result<Vec<Name>>` |
| `Get`                          | `fn get_name(&self, id: &EntityId) -> Result<Option<Name>>`                      |
| `GetMulti`                     | `fn get_name_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Name>>>`        |
| `GetAll`                       | `fn get_all_name(&self) -> Result<Vec<Name>>`                                    |
| `Update`                       | `fn update_name(&self, entity: &Name) -> Result<Name>`                           |
| `UpdateMulti`                  | `fn update_name_multi(&self, entities: &[Name]) -> Result<Vec<Name>>`            |
| `UpdateWithRelationships`      | `fn update_name_with_relationships(&self, entity: &Name) -> Result<Name>`                           |
| `UpdateWithRelationshipsMulti` | `fn update_name_with_relationships_multi(&self, entities: &[Name]) -> Result<Vec<Name>>`            |
| `Remove`                       | `fn remove_name(&self, id: &EntityId) -> Result<()>`                             |
| `RemoveMulti`                  | `fn remove_name_multi(&self, ids: &[EntityId]) -> Result<()>`                    |
| `GetRelationship`              | `fn get_name_relationship(&self, id: &EntityId, field: &RF) -> Result<Vec<EntityId>>` |
| `GetRelationshipMany`          | `fn get_name_relationship_many(&self, ids: &[EntityId], field: &RF) -> Result<HashMap<EntityId, Vec<EntityId>>>` |
| `GetRelationshipCount`         | `fn get_name_relationship_count(&self, id: &EntityId, field: &RF) -> Result<usize>` |
| `GetRelationshipInRange`       | `fn get_name_relationship_in_range(&self, id: &EntityId, field: &RF, offset: usize, limit: usize) -> Result<Vec<EntityId>>` |
| `GetRelationshipsFromRightIds` | `fn get_name_relationships_from_right_ids(&self, field: &RF, right_ids: &[EntityId]) -> Result<Vec<(EntityId, Vec<EntityId>)>>` |
| `SetRelationship`              | `fn set_name_relationship(&self, id: &EntityId, field: &RF, right_ids: &[EntityId]) -> Result<()>` |
| `SetRelationshipMulti`         | `fn set_name_relationship_multi(&self, field: &RF, relationships: Vec<(EntityId, Vec<EntityId>)>) -> Result<()>` |
| `MoveRelationship`             | `fn move_name_relationship(&self, id: &EntityId, field: &RF, ids_to_move: &[EntityId], new_index: i32) -> Result<Vec<EntityId>>` |
| `Snapshot`                     | `fn snapshot_name(&self, ids: &[EntityId]) -> Result<EntityTreeSnapshot>`        |
| `Restore`                      | `fn restore_name(&self, snap: &EntityTreeSnapshot) -> Result<()>`                |

**Read-only actions** (use with `QueryUnitOfWork`):

| Action                          | Generated method signature                                                        |
|---------------------------------|-----------------------------------------------------------------------------------|
| `GetRO`                         | `fn get_name(&self, id: &EntityId) -> Result<Option<Name>>`                      |
| `GetMultiRO`                    | `fn get_name_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Name>>>`        |
| `GetAllRO`                      | `fn get_all_name(&self) -> Result<Vec<Name>>`                                    |
| `GetRelationshipRO`             | `fn get_name_relationship(&self, id: &EntityId, field: &RF) -> Result<Vec<EntityId>>` |
| `GetRelationshipManyRO`         | `fn get_name_relationship_many(&self, ids: &[EntityId], field: &RF) -> Result<HashMap<EntityId, Vec<EntityId>>>` |
| `GetRelationshipCountRO`        | `fn get_name_relationship_count(&self, id: &EntityId, field: &RF) -> Result<usize>` |
| `GetRelationshipInRangeRO`      | `fn get_name_relationship_in_range(&self, id: &EntityId, field: &RF, offset: usize, limit: usize) -> Result<Vec<EntityId>>` |
| `GetRelationshipsFromRightIdsRO`| `fn get_name_relationships_from_right_ids(&self, field: &RF, right_ids: &[EntityId]) -> Result<Vec<(EntityId, Vec<EntityId>)>>` |

> Do not mix read-only (`*RO`) and write actions in the same unit of work.

**For long operations**, add `thread_safe = true` to the **implementation** attributes (not the trait). This makes the generated code use `Mutex` instead of `RefCell` for thread safety.

### Full Example

Given a "Save" use case in the "HandlingManifest" feature that needs to read and update `Work` and `Setting` entities:

**Use case + trait** (`use_cases/save_uc.rs`):

The developer only have to adapt the `#[macros::uow_action]` attributes to expose only the entity operations your use case needs. Then, implement the use case logic.

```rust
use common::database::CommandUnitOfWork;

pub trait SaveUnitOfWorkFactoryTrait {
    fn create(&self) -> Box<dyn SaveUnitOfWorkTrait>;
}

// Adapt these macros to your needs:
#[macros::uow_action(entity = "Work", action = "Get")]
#[macros::uow_action(entity = "Work", action = "Update")]
#[macros::uow_action(entity = "Setting", action = "Get")]
#[macros::uow_action(entity = "Setting", action = "GetMulti")]
pub trait SaveUnitOfWorkTrait: CommandUnitOfWork {
    fn publish_save_event(&self, ids: Vec<EntityId>, data: Option<String>);
}

pub struct SaveUseCase {
    uow_factory: Box<dyn SaveUnitOfWorkFactoryTrait>,
}

impl SaveUseCase {
    pub fn new(uow_factory: Box<dyn SaveUnitOfWorkFactoryTrait>) -> Self {
        SaveUseCase { uow_factory }
    }

    pub fn execute(&mut self, dto: &SaveDto) -> Result<SaveResultDto> {
        let mut uow = self.uow_factory.create();
        uow.begin_transaction()?;

        // Use the UoW methods you declared:
        let work = uow.get_work(&dto.work_id)?
            .ok_or_else(|| anyhow!("Work not found"))?;

        let setting = uow.get_setting(&dto.setting_id)?
            .ok_or_else(|| anyhow!("Setting not found"))?;

        // ... your business logic ...

        let updated_work = uow.update_work(&work)?;

        uow.commit()?;

        // Emit the feature event (adapt ids/data to your needs):
        uow.publish_save_event(vec![], None);

        Ok(SaveResultDto { /* ... */ })
    }
}
```

**Implementation** (`units_of_work/save_uow.rs`):

The developer only have to adapt the `#[macros::uow_action]` attributes to expose only the entity operations your use case needs.

```rust
use crate::use_cases::save_uc::{SaveUnitOfWorkFactoryTrait, SaveUnitOfWorkTrait};
use common::database::CommandUnitOfWork;

pub struct SaveUnitOfWork {
    context: DbContext,
    transaction: Option<Transaction>,
    event_hub: Arc<EventHub>,
    event_buffer: RefCell<EventBuffer>,
}

impl SaveUnitOfWork {
    pub fn new(db_context: &DbContext, event_hub: &Arc<EventHub>) -> Self {
        SaveUnitOfWork {
            context: db_context.clone(),
            transaction: None,
            event_hub: event_hub.clone(),
            event_buffer: RefCell::new(EventBuffer::new()),
        }
    }
}

impl CommandUnitOfWork for SaveUnitOfWork {
    fn begin_transaction(&mut self) -> Result<()> {
        self.transaction = Some(Transaction::begin_write_transaction(&self.context)?);
        self.event_buffer.get_mut().begin_buffering();
        Ok(())
    }

    fn commit(&mut self) -> Result<()> {
        self.transaction
            .take()
            .ok_or_else(|| anyhow!("No active transaction to commit"))?
            .commit()?;
        for event in self.event_buffer.get_mut().flush() {
            self.event_hub.send_event(event);
        }
        Ok(())
    }

    fn rollback(&mut self) -> Result<()> {
        self.transaction
            .take()
            .ok_or_else(|| anyhow!("No active transaction to rollback"))?
            .rollback()?;
        self.event_buffer.get_mut().discard();
        Ok(())
    }

    fn create_savepoint(&self) -> Result<types::Savepoint> {
        self.transaction
            .as_ref()
            .ok_or_else(|| anyhow!("No active transaction for savepoint"))?
            .create_savepoint()
    }

    fn restore_to_savepoint(&mut self, savepoint: types::Savepoint) -> Result<()> {
        let mut transaction = self.transaction
            .take()
            .ok_or_else(|| anyhow!("No active transaction to restore"))?;
        transaction.restore_to_savepoint(savepoint)?;
        self.event_buffer.get_mut().discard();
        self.event_hub.send_event(Event {
            origin: Origin::DirectAccess(DirectAccessEntity::All(AllEvent::Reset)),
            ids: vec![],
            data: None,
        });
        self.transaction = Some(transaction);
        Ok(())
    }
}

// Same macros as the trait, matching exactly:
#[macros::uow_action(entity = "Work", action = "Get")]
#[macros::uow_action(entity = "Work", action = "Update")]
#[macros::uow_action(entity = "Setting", action = "Get")]
#[macros::uow_action(entity = "Setting", action = "GetMulti")]
impl SaveUnitOfWorkTrait for SaveUnitOfWork {
    fn publish_save_event(&self, ids: Vec<EntityId>, data: Option<String>) {
        self.event_hub.send_event(Event {
            origin: Origin::HandlingManifest(Save),
            ids,
            data,
        });
    }
}

pub struct SaveUnitOfWorkFactory {
    context: DbContext,
    event_hub: Arc<EventHub>,
}

impl SaveUnitOfWorkFactory {
    pub fn new(db_context: &DbContext, event_hub: &Arc<EventHub>) -> Self {
        SaveUnitOfWorkFactory {
            context: db_context.clone(),
            event_hub: event_hub.clone(),
        }
    }
}

impl SaveUnitOfWorkFactoryTrait for SaveUnitOfWorkFactory {
    fn create(&self) -> Box<dyn SaveUnitOfWorkTrait> {
        Box::new(SaveUnitOfWork::new(&self.context, &self.event_hub))
    }
}
```

### Transaction Methods

These are available on every UoW via `CommandUnitOfWork` or `QueryUnitOfWork`.

**CommandUnitOfWork** (read-write):

| Method                                      | Purpose                                                            |
|---------------------------------------------|--------------------------------------------------------------------|
| `begin_transaction(&mut self)`              | Start a write transaction and begin event buffering                |
| `commit(&mut self)`                         | Commit; flush buffered events on success                           |
| `rollback(&mut self)`                       | Roll back the transaction and discard buffered events              |
| `create_savepoint(&self)`                   | Create a savepoint within the current transaction                  |
| `restore_to_savepoint(&mut self, sp)`       | Restore to a savepoint, discard events, emit Reset                 |

Do not use savepoint without understanding the implications: please read [Undo-Redo Architecture # savepoints](undo-redo-architecture.md#savepoints)

**QueryUnitOfWork** (read-only):

| Method                        | Purpose                              |
|-------------------------------|--------------------------------------|
| `begin_transaction(&self)`    | Start a read transaction             |
| `end_transaction(&self)`      | End the read transaction             |

The event buffering ensures that if a transaction fails, no events are emitted and the UI stays consistent.

### Undoable Custom Use Cases

If a custom use case is marked `undoable: true` in the manifest, the generated scaffold includes an `UndoRedoCommand` impl:

```rust
impl UndoRedoCommand for SaveUseCase {
    fn undo(&mut self) -> Result<()> {
        // TODO: implement undo logic
        unimplemented!();
    }

    fn redo(&mut self) -> Result<()> {
        // TODO: implement redo logic
        unimplemented!();
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}
```

You implement the `undo()` and `redo()` methods. The controller will call `undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)` after a successful `execute()`.