qleany 1.7.1

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
# Generated Infrastructure - C++/Qt

This document details the infrastructure Qleany generates for C++20/Qt6. It's a reference material — read it when you need to understand, extend, or debug the generated code, not as a getting-started guide.

## C++/Qt Infrastructure

### Database Layer

**DbContext / DbSubContext**: Connection pool with scoped transactions. Each unit of work owns a `DbSubContext` providing `beginTransaction`, `commit`, `rollback`, and savepoint support. Savepoint is present just in case the developer really needs it, Qleany doesn't use it internally (see the undo redo documentation if you want to know why)

**Repository Factory**: Creates repositories bound to a specific `DbSubContext` and `EventRegistry`. Returns owned instances (`std::unique_ptr`) — no cross-thread sharing. Every command/query holds its own `DbSubContext`, table, and repository instances.

```cpp
auto repo = RepositoryFactory::createWorkRepository(m_dbSubContext, m_eventRegistry);
auto works = repo->get(QList<int>{workId});
```

**Table Cache / Junction Cache**: Thread-safe, time-expiring (30 minutes), invalidated at write time. Improves performance for repeated queries within a session.

### SQLite Configuration

SQLite with WAL mode, optimized for desktop applications:

```sql
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;
PRAGMA cache_size=20000;        -- 20MB
PRAGMA mmap_size=268435456;     -- 256MB
```

These are tuned for a typical desktop workload: frequent reads with occasional write bursts, and a single user.

**WAL mode** lets reads and writes happen concurrently instead of blocking each other. This keeps the UI responsive while background operations write to the database. The tradeoff is two sidecar files (-wal, -shm) next to the database, harmless for a local application.

**NORMAL synchronous** skips fsync() on every commit. A power failure could lose the last transaction, but the database won't corrupt. For a desktop app where users already expect to lose unsaved work after a crash, this is a good trade for faster writes.

**20 MB page cache** (~10× the default) keeps hot pages in memory so navigation stays snappy. Negligible cost on any modern machine.

**256 MB memory-map** lets the OS map the database file into the process address space, bypassing SQLite's I/O layer for reads. Since most desktop databases stay well under this limit, the entire file benefits. Works well on Linux and macOS; functional on Windows, though less battle-tested there.

Together, these give fast, responsive data access while accepting a durability tradeoff that simply doesn't matter for a single-user desktop application.

### List Field Storage

Entity fields marked `is_list: true` in the manifest are stored as JSON arrays in SQLite TEXT columns. On read, the JSON array is deserialized back into `QList<T>`. This applies to all primitive list types (`QList<QString>`, `QList<int>`, `QList<float>`, `QList<uint>`, `QList<bool>`, `QList<QUuid>`, `QList<QDateTime>`).

For `QList<QUuid>` and `QList<QDateTime>`, `QMetaType` converters are registered at startup (in `converter_registration.h`) to handle the `QList<T>` ↔ `QVariantList` round-trip required by QML property binding and `QSqlQuery` value conversion.

### Ephemeral Database Pattern

The internal database lives in `/tmp/`, decoupled from user files:

1. **Load**: Transform file → internal database
2. **Work**: All operations against ephemeral database
3. **Save**: Transform internal database → file

This pattern separates the user's file format from internal data structures. Your `.myapp` file can be JSON, XML, SQLite, or any format. The internal database remains consistent.

The user must implement this pattern in dedicated custom use cases.

Note: the SQLite database can be set to exist in memory by using `:memory:` as the filename.

### Long Operation Manager

Threaded execution for heavy tasks. When a use case is marked `long_operation: true` in the manifest, the controller bypasses the coroutine-based command pipeline and instead delegates to a `LongOperationManager` that runs the work on a background thread via `QtConcurrent::run`.

Operations implement the `ILongOperation` interface:

```cpp
class ILongOperation
{
  public:
    virtual ~ILongOperation() = default;

    virtual QJsonObject execute(
        std::function<void(OperationProgress)> progressCallback,
        const std::atomic<bool> &cancelFlag) = 0;
};
```

Progress is reported via an `OperationProgress` Q_GADGET (usable from QML):

```cpp
struct OperationProgress
{
    Q_GADGET
    Q_PROPERTY(int current MEMBER current)
    Q_PROPERTY(int total MEMBER total)
    Q_PROPERTY(QString message MEMBER message)
    Q_PROPERTY(double percentage READ percentage)

    int current = 0;
    int total = 0;
    QString message;

    double percentage() const
    { return total > 0 ? (static_cast<double>(current) / total) * 100.0 : 0.0; }
};
```

The controller starts the operation synchronously (no `co_await`) and returns an operation ID:

```cpp
QString MyFeatureController::doHeavyWork(const HeavyWorkDto &dto)
{
    auto uow = std::make_unique<HeavyWorkUnitOfWork>(*m_dbContext, m_eventRegistry, m_featureEventRegistry);
    auto operation = std::make_shared<HeavyWorkUseCase>(std::move(uow), dto);
    return m_longOperationManager->startOperation(std::move(operation));
}
```

Progress and results can be polled by the caller:

```cpp
std::optional<OperationProgress> MyFeatureController::getDoHeavyWorkProgress(const QString &operationId) const
{
    return m_longOperationManager->getProgress(operationId);
}

std::optional<HeavyWorkResultDto> MyFeatureController::getDoHeavyWorkResult(const QString &operationId) const
{
    auto result = m_longOperationManager->getResult(operationId);
    if (!result.has_value())
        return std::nullopt;
    return gadgetFromJson<HeavyWorkResultDto>(result.value());
}
```

The `LongOperationManager` also emits signals for reactive consumption:

| Signal                                             | When                          |
|----------------------------------------------------|-------------------------------|
| `progressChanged(operationId, progress)`           | Progress callback fires       |
| `operationCompleted(operationId, resultJson)`      | Operation finished normally   |
| `operationFailed(operationId, errorString)`        | Operation threw an error      |
| `operationCancelled(operationId)`                  | Cancellation was acknowledged |

Cancellation is cooperative: the manager sets an `std::atomic<bool>` flag that the operation should check periodically.

```cpp
m_longOperationManager->cancelOperation(operationId);
```

Features:
- Background execution via `QtConcurrent::run`
- Progress callbacks marshalled to the main thread
- Cancellation support via atomic flag
- Result or error on completion
- QML-compatible progress via Q_GADGET

### Async Undo/Redo with QCoro

Controllers use C++20 coroutines via QCoro for non-blocking command execution:

```cpp
QCoro::Task<QList<WorkDto>> WorkController::update(const QList<WorkDto> &works, int stackId)
{
    if (!m_undoRedoSystem)
    {
        qCritical() << "UndoRedo system not available";
        co_return QList<WorkDto>();
    }

    // Create use case that will be owned by the command
    std::unique_ptr<IWorkUnitOfWork> uow = std::make_unique<WorkUnitOfWork>(*m_dbContext, m_eventRegistry);
    auto useCase = std::make_shared<UpdateWorkUseCase>(std::move(uow));

    // Create command that owns the use case
    auto command = std::make_shared<Common::UndoRedo::UndoRedoCommand>("Update Works Command"_L1);
    QList<WorkDto> result;

    // Create weak_ptr to break circular reference
    std::weak_ptr<UpdateWorkUseCase> weakUseCase = useCase;

    // Prepare lambda for execute - use weak_ptr to avoid circular reference
    command->setExecuteFunction([weakUseCase, works, &result](auto &) {
        if (auto useCase = weakUseCase.lock())
        {
            result = useCase->execute(works);
        }
    });

    // Prepare lambda for redo - use weak_ptr to avoid circular reference
    command->setRedoFunction([weakUseCase]() -> Common::UndoRedo::Result<void> {
        if (auto useCase = weakUseCase.lock())
        {
            return useCase->redo();
        }
        return Common::UndoRedo::Result<void>("UseCase no longer available"_L1,
                                              Common::UndoRedo::ErrorCategory::ExecutionError);
    });

    // Prepare lambda for undo - use weak_ptr to avoid circular reference
    command->setUndoFunction([weakUseCase]() -> Common::UndoRedo::Result<void> {
        if (auto useCase = weakUseCase.lock())
        {
            return useCase->undo();
        }
        return Common::UndoRedo::Result<void>("UseCase no longer available"_L1,
                                              Common::UndoRedo::ErrorCategory::ExecutionError);
    });

    // Store the useCase in the command to maintain ownership
    // This ensures the useCase stays alive as long as the command exists
    command->setProperty("useCase", QVariant::fromValue(useCase));

    // Execute command asynchronously using QCoro integration
    std::optional<bool> success = co_await m_undoRedoSystem->executeCommandAsync(command, 500, stackId);

    if (!success.has_value())
    {
        qWarning() << "Update work command execution timed out";
        co_return QList<WorkDto>();
    }

    if (!success.value())
    {
        qWarning() << "Failed to execute update work command";
        co_return QList<WorkDto>();
    }

    co_return result;
}
```

What was written above is the "flattened" version of the real code. The actual code below  uses helper functions to make it more readable and less repetitive (in the file `common/controller_command_helpers.h`). Thus, this same code can be shortened:

```cpp

QCoro::Task<QList<WorkDto>> WorkController::update(const QList<WorkDto> &files, int stackId)
{
    auto uow = std::make_unique<WorkUnitOfWork>(*m_dbContext, m_eventRegistry);
    auto useCase = std::make_shared<UpdateWorkUseCase>(std::move(uow));

    co_return co_await Helpers::executeUndoableCommand<QList<WorkDto>>(
        m_undoRedoSystem, u"Update works Command"_s, std::move(useCase), stackId, kDefaultCommandTimeoutMs, files);
}
```

After v1.0.34, use case templates classes in `common/direct_access/use_case_helpers/` were introduced to simplify the code further.

```

using UpdateUC = UCH::UpdateUseCase<SCE::Work, WorkDto, DtoMapper, IWorkUnitOfWork, false>;

QCoro::Task<QList<WorkDto>> WorkController::update(const QList<WorkDto> &works)
{
    auto uow = std::make_unique<WorkUnitOfWork>(*m_dbContext, m_eventRegistry);
    auto useCase = std::make_shared<UpdateUC>(std::move(uow));

    co_return co_await Helpers::executeNotUndoableCommand<QList<WorkDto>>(
        m_undoRedoSystem, u"Update works Command"_s, std::move(useCase), kDefaultCommandTimeoutMs,
        [this](const QString &cmd, const QString &msg) {
            if (m_eventRegistry)
                m_eventRegistry->publishError(cmd, msg);
        },
        works);
}

```

Use cases contain synchronous business logic with state for undo/redo. This code is roughly equivalent of what the helper template classes offer:

```cpp
QList<WorkDto> UpdateWorkUseCase::execute(const QList<WorkDto> &works)
{
{
    if (m_hasExecuted)
        return m_updatedDtos;


    try
    {
    
        // Store original state for undo
        m_uow->beginTransaction();
        m_originalWorks = DtoMapper::toDtoList(m_uow->getWork(workIds));
    
        // Perform update
        auto updatedEntities = m_uow->updateWork(DtoMapper::toEntityList(works));
        m_uow->commit();
    
        m_updatedWorks = DtoMapper::toDtoList(updatedEntities);
            
    catch (...)
    {
        m_uow->rollback();
        throw;
    }
     return m_updatedWorks;

}

Result<void> UpdateWorkUseCase::undo()
{

    try
    {
        m_uow->beginTransaction();
        m_uow->updateWork(DtoMapper::toEntityList(m_originalWorks));
        m_uow->commit();
        return {};
    
    catch (...)
    {
        m_uow->rollback();
        throw;
    }
}

Result<void> UpdateWorkUseCase::redo()
{
...
}

```

Note the try/catch blocks in the use case. It ensures that the transaction is rolled back in case of an exception. When rolled back, the SignalBuffer is cleared. When committed, the SignalBuffer emits the events. This ensures that if an exception is thrown, no events are emitted and the UI remains in a consistent state.

Undoable feature use cases are to be customized by the user, following the same pattern. No template classes here.

Queries (read-only operations) also execute asynchronously:

```cpp

using GetUC = UCH::GetUseCase<WorkDto, DtoMapper, IWorkUnitOfWork>;

QCoro::Task<QList<WorkDto>> WorkController::get(const QList<int> &workIds)
{

        co_return co_await Helpers::executeReadQuery<QList<WorkDto>>(
    m_undoRedoSystem, u"Get works Query"_s, [this, workIds]() -> QList<WorkDto> {
        auto uow = std::make_unique<WorkUnitOfWork>(*m_dbContext, m_eventRegistry);
        auto useCase = std::make_unique<GetUC>(std::move(uow));
        return useCase->execute(workIds);
    });

}
```

Features:
- Undo stacks (per-document undo)
- Command grouping (multiple operations as one undo step)
- Timeout handling for long operations
- Signal emission only on a successful commit

### Event Registry

QObject-based event dispatch for reactive updates. Each entity has its own events class:

```cpp
class WorkEvents : public QObject
{
    Q_OBJECT
public:
    explicit WorkEvents(QObject *parent = nullptr) : QObject(parent)
    {
        // Register metatypes for cross-thread signal delivery
        qRegisterMetaType<QList<int>>("QList<int>");
        qRegisterMetaType<WorkRelationshipField>("WorkRelationshipField");
    }

public Q_SLOTS:
    // Invoked from any thread via QMetaObject::invokeMethod
    void publishCreated(const QList<int> &ids) { Q_EMIT created(ids); }
    void publishUpdated(const QList<int> &ids) { Q_EMIT updated(ids); }
    void publishRemoved(const QList<int> &ids) { Q_EMIT removed(ids); }
    void publishRelationshipChanged(int workId, WorkRelationshipField relationship, 
                                    const QList<int> &relatedIds)
    { Q_EMIT relationshipChanged(workId, relationship, relatedIds); }

Q_SIGNALS:
    void created(const QList<int> &ids);
    void updated(const QList<int> &ids);
    void removed(const QList<int> &ids);
    void relationshipChanged(int workId, WorkRelationshipField relationship, 
                             const QList<int> &relatedIds);
};
```

Repositories emit events asynchronously via queued connections to ensure thread safety:

```cpp
// In repository
void WorkRepository::emitUpdated(const QList<int> &ids) const
{
    if (!m_events || ids.isEmpty())
        return;
    QMetaObject::invokeMethod(m_events, "publishUpdated", 
                              Qt::QueuedConnection, Q_ARG(QList<int>, ids));
}

// Subscribing (C++):
connect(s_serviceLocator->eventRegistry()->workEvents(), &WorkEvents::updated, this, &Whatever::onWorkUpdated);
```

```qml
// Subscribing (QML)
Connections {
    target: EventRegistry.workEvents()
    function onWorkUpdated(ids) { doSomething(ids) }
}
```

The `EventRegistry`  provides access to all entity events from both C++ and QML.

A similar pattern is used for `FeatureEventRegistry` in `common/features/feature_event_registry.h`. Each feature (= group of use cases) has its own events class.

```cpp
// Subscribing (C++):
connect(s_serviceLocator->eventFeatureRegistry()->handlingAppLifecycleEvents(), &HandlingAppLifecycleEvents::initializeAppSignal, this, &Whatever::onInitializeAppSignal);
```
```qml
// Subscribing (QML)
Connections {
target: EventFeatureRegistry.handlingAppLifecycleEvents()
function onInitializeAppSignal() { doSomething() }
}
```
---

### Repository

Generated repositories are batch-capable interfaces. One repository for each entity type.

| Method                                         | Purpose                                            |
|------------------------------------------------|----------------------------------------------------|
| `create(QList<Entity>)`                        | Insert new entities                                |
| `createOrphans(QList<Entity>, ownerId, index)` | Insert new entities and attach them to their owner |
| `get(QList<int>)`                              | Fetch entities by IDs                              |
| `getAll()`                                     | Fetch all entities (use with caution)              |
| `update(QList<Entity>)`                        | Update scalar fields only                          |
| `updateWithRelationships(QList<Entity>)`       | Update scalar fields and relationships             |
| `remove(QList<int>)`                           | Delete entities (cascade for strong relationships) |
| `snapshot(QList<int> ids)`                     | Get a snapshot of the entities for undo/redo       |
| `restore(EntityTreeSnapshot)`                  | Restore entity from snapshot                       |

Relationship-specific methods (if the entity has relationships):

| Method                                                | Purpose                         |
|-------------------------------------------------------|---------------------------------|
| `getRelationshipIds(id, field)`                       | Get related IDs for one entity  |
| `getRelationshipIdsMany(ids, field)`                  | Batch lookup                    |
| `setRelationshipIds(id, field, ids)`                  | Set relationship for one entity |
| `moveRelationshipIds(id, field, idsToMove, newIndex)` | Move IDs to a new position within an ordered relationship |
| `getRelationshipIdsCount(id, field)`                  | Count related items             |
| `getRelationshipIdsInRange(id, field, offset, limit)` | Paginated access                |

C++/Qt offers additional pagination and counting methods for UI scenarios. The generated QAbstractListModels aren't using these yet but can be extended to do so.


### Unit of Work

In C++/Qt, the units of work are helped by macros and inherited classes to generate all the boilerplate for transaction management and repository access. This can be a debatable design choice, since all is already generated by Qleany. The reality is: not all can be generated. The user (developer) has the responsibility to adapt the units of work for each custom use case. The macros are here to ease this task.

> The user is to adapt the macros in custom use cases.

No factory for the unit of work here, the controller creates a new unit of work per use case.

The examples are from Qleany's own code. The C++ parts are generated from the same manifest file.

Here is the unit of work used by all CRUD use cases of the `Workspace` entity. You can see SCUoW::EntityFullImpl
inheritance, which refactors all the boilerplate of all entities units of work into a single base template class (in `common/unit_of_work/uow_ops.h` file).

```cpp
// Unit of work encapsulates repository access 
class WorkspaceUnitOfWork final
    : public SCUoW::UnitOfWorkBase,
      public SCUoW::EntityFullImpl<WorkspaceUnitOfWork, SCE::Workspace, SCDWorkspace::WorkspaceRelationshipField>
{
    friend class SCUoW::EntityFullImpl<WorkspaceUnitOfWork, SCE::Workspace, SCDWorkspace::WorkspaceRelationshipField>;

  public:
    WorkspaceUnitOfWork(SCDatabase::DbContext &dbContext, const QPointer<SCD::EventRegistry> &eventRegistry)
        : UnitOfWorkBase(dbContext, eventRegistry)
    {
    }
    ~WorkspaceUnitOfWork() override = default;

    /// Called by CRTP base to get a repository for Workspace.
    [[nodiscard]] auto makeRepository()
    {
        return SCD::RepositoryFactory::createWorkspaceRepository(m_dbSubContext, m_eventRegistry);
    }
};

// In controller :

QCoro::Task<QList<WorkDto>> WorkController::create(const QList<CreateWorkDto> &works)
{
...
    auto uow = std::make_unique<WorkUnitOfWork>(*m_dbContext, m_eventRegistry);
    auto useCase = std::make_shared<CreateWorkUseCase>(std::move(uow));
...
}
```

This keeps transaction boundaries explicit and testable.

For the custom use cases, things are done a bit differently to make adaptation easier. We are using dumb variadic macros here:

```cpp


class SaveUnitOfWork : public Common::UnitOfWork::UnitOfWorkBase, public ISaveUnitOfWork
{
  public:
    SaveUnitOfWork(SCDatabase::DbContext &db, QPointer<SCD::EventRegistry> eventRegistry,
                   QPointer<SCF::FeatureEventRegistry> featureEventRegistry)
        : UnitOfWorkBase(db, eventRegistry), m_featureEventRegistry(featureEventRegistry)
    {
    }

    /* TODO: adapt entities to real use :
     * Available Atomic Macros (uow_macros.h — for custom UoWs):
     *   Interface:    UOW_ENTITY_{CREATE,GET,UPDATE,REMOVE,CRUD}(Name)
     *                 UOW_ENTITY_RELATIONSHIPS(Name, Rel)
     *
     * The equivalent macros (with the DECLARE_ prefix) must be set in the use case's unit of work interface file
     * in use_cases/i_save_uow.h
     */
    UOW_ENTITY_UPDATE(Dto);
    UOW_ENTITY_UPDATE(DtoField);
    UOW_ENTITY_UPDATE(Global);
    UOW_ENTITY_UPDATE(Relationship);
    UOW_ENTITY_UPDATE(Work);
    UOW_ENTITY_UPDATE(Entity);
    UOW_ENTITY_UPDATE(Field);
    UOW_ENTITY_UPDATE(Feature);
    UOW_ENTITY_UPDATE(UseCase);

    void publishSaveSignal() override;

  private:
    QPointer<SCF::FeatureEventRegistry> m_featureEventRegistry;
};

inline void SaveUnitOfWork::publishSaveSignal()
{
    m_featureEventRegistry->handlingManifestEvents()->publishSaveSignal();
}

```

The interface in `handling_manifest/use_cases/save_uc/i_save_uow.h`:

```cpp

class ISaveUnitOfWork : public virtual Common::UnitOfWork::ITransactional
{
  public:
    ~ISaveUnitOfWork() override = default;

    /* TODO: adapt entities to real use :
     * Available Atomic Macros (uow_macros.h — for custom UoWs):
     *   Interface:    DECLARE_UOW_ENTITY_{CREATE,GET,UPDATE,REMOVE,CRUD}(Name)
     *                 DECLARE_UOW_ENTITY_RELATIONSHIPS(Name, Rel)
     *
     * The equivalent macros (without the DECLARE_ prefix) must be set in the use case's unit of work file
     * in units_of_work/save_uow.h
     */
    DECLARE_UOW_ENTITY_UPDATE(Dto);
    DECLARE_UOW_ENTITY_UPDATE(DtoField);
    DECLARE_UOW_ENTITY_UPDATE(Global);
    DECLARE_UOW_ENTITY_UPDATE(Relationship);
    DECLARE_UOW_ENTITY_UPDATE(Root);
    DECLARE_UOW_ENTITY_UPDATE(Entity);
    DECLARE_UOW_ENTITY_UPDATE(Field);
    DECLARE_UOW_ENTITY_UPDATE(Feature);
    DECLARE_UOW_ENTITY_UPDATE(UseCase);

    virtual void publishSaveSignal() = 0;
};
```

### DTO Mapping

DTOs are generated for boundary crossings between UI and use cases. DTO←→Entity conversion is done in the use cases:

```
|----------------DTO-------------------|------------------Entity----------|
UI ←→ Controller ←→ CreateCarDto ←→ UseCase ←→ Car (Entity) ←→ Repository
```

The separation ensures:
- Controllers don't expose entity internals
- You control what data flows in/out of each layer

The DTOs are C++20 struct aggregates that are enhanced by Q_GADGET macro. They are usable in QML.

---

## File Organization

CMakeLists.txt are disseminated across the project and are not shown here.

```
src/
├── common/
│   ├── service_locator.h/.cpp          # global access to shared services (DbContext, EventRegistry, etc.)
│   ├── controller_command_helpers.h    # helper functions for controller command execution
│   ├── signal_buffer.h                 # SignalBuffer for undo/redo
│   ├── database/                           # database infrastructure
│   │   ├── junction_table_ops/...
│   │   ├── db_builder.h
│   │   ├── db_context.h
│   │   └── table_cache.h
│   ├── entities/                      # Generated entities
│   │   ├── my_entity.h
│   │   └── ...
│   ├── unit_of_work/                  # unit of work macros and base class
│   │   ├── unit_of_work.h
│   │   ├── uow_macros.h 
│   │   └── ...
│   ├── features/
│   │   ├── feature_event_registry.h   # Event registry for feature events
│   │   └── ...
│   ├── direct_access/                     # Holds the repositories and tables
│   │   ├── use_case_helpers/...          # Template classes for direct access use cases
│   │   ├── repository_factory.h/.cpp
│   │   ├── event_registry.h
│   │   └── {entity}/
│   │       ├── i_{entity}_repository.h   # Interface with relationship enum
│   │       ├── table_definitions.h       #  Table schema definitions
│   │       ├── {entity}_repository.h/.cpp
│   │       ├── {entity}_table.h/.cpp
│   │       └── {entity}_events.h
│   └── undo_redo/ ...                      # undo/redo infrastructure
├── direct_access/                          # Direct access to entity controllers and use cases
│   └── {entity}/
│       ├── {entity}_controller.h/.cpp      # entry point for direct access to this entity
│       ├── dtos.h
│       ├── {entity}_unit_of_work.h
│       ├── i_{entity}_unit_of_work.h
│       └── dto_mapper.h
├── {feature}/                              # Custom controllers and use cases
│    ├── {feature}_controller.h/.cpp
│    ├── {feature}_dtos.h
│    ├── units_of_work/                    
│    │   └── {use case}_uow.h                # ← adapt the macros here
│    └── use_cases/              
│        ├── {use case}_uc/                  # store here the use case's companion modules
│        │   └── i_{use case}_uow.h          # ← adapt the macros here too
│       └── {use case}_uc.h/.cpp            # ← You implement the logic here
│ 
├── qtwidgets_ui 
│   ├── main.cpp
│   └── main_window.h/.cpp                          # ← write your UI here
│
├── presentation                                        # generated for all QML-based UIs
│   ├── CMakeLists.txt
│   ├── mock_imports                                    # QML mocks
│   │   └── Car
│   │       ├── Controllers/ ... 
│   │       ├── Models/ ... 
│   │       └── Singles/ ... 
│   └── real_imports                                 # QML real imports
│       ├── controllers/ ... 
│       ├── models/ ... 
│       └── singles/ ... 
└── qtquick_app
    ├── {My App 3 first letters}/ ...               # ← write your UI here
    ├── content/ ...                                # ← and here
    ├── main.cpp
    ├── main.qml
    └── qtquickcontrols2.conf
```