Add Repository<T> interface + ITemporalEntity + IHistoryRepository<T> #7

Closed
opened 2026-04-27 21:30:16 +02:00 by uwe.admin · 4 comments
Owner

Migrated from uwe.admin/webapp-scaffold#8 per the Option A decision (C++ work belongs in oatpp-authkit, not the TypeScript frontend scaffold).

Implementation lands under oatpp-authkit/include/oatpp-authkit/repo/ alongside auth/, db/, dto/, handler/. Tests under oatpp-authkit/test/ matching the existing plain-executable + REQUIRE-macro style of test_negotiation.cpp / test_json_serialization.cpp. README v0.1 table appended.


Part of a structural refactor: move from per-entity *Db clients to a shared Repository<T> abstraction. This issue lands the interfaces only in oatpp-authkit. No behavior change yet, no concrete implementations, no callers updated.

Scope

Add to webapp-scaffold:

  • Repository<TDto> — pure abstract interface, temporal-agnostic:
    • findByEntityId(entityId)
    • list(filter)
    • save(dto) — generates a random UUID if entity_id is null on the DTO; otherwise uses the caller-supplied id (mixed allocation policy)
    • softDelete(entityId)
  • ITemporalEntity — DTO mixin/marker requiring entity_id, valid_from, valid_until
  • IHistoryRepository<TDto>separate interface for history(entityId), only implemented by repositories that wrap temporal entities
  • TemporalAt — value type: "live" or a specific point in time
  • ActorContext — placeholder for who is performing the action (used by the scope-guard decorator in the next issue)

Out of scope

  • Any concrete repository implementations
  • Decorators (temporal, scope-guard) — separate issue
  • Transactions / unit-of-work — explicitly deferred
  • Any migration of controllers or *Db callers in fewo-webapp

Acceptance

  • Interfaces compile in oatpp-authkit
  • A trivial in-memory test fake of Repository<MockDto> exists, demonstrating the contract without touching SQL

Decisions (from planning discussion)

  1. entity_id allocation: mixed — client passes id OR repository allocates a random UUID if null
  2. UnitOfWork: deferred, not in scope
  3. Repository<T> shape: pure abstract interface (not C++20 concept)
  4. History queries: live on a separate IHistoryRepository<T>, not on Repository<T>
Migrated from uwe.admin/webapp-scaffold#8 per the Option A decision (C++ work belongs in oatpp-authkit, not the TypeScript frontend scaffold). Implementation lands under `oatpp-authkit/include/oatpp-authkit/repo/` alongside `auth/`, `db/`, `dto/`, `handler/`. Tests under `oatpp-authkit/test/` matching the existing plain-executable + REQUIRE-macro style of `test_negotiation.cpp` / `test_json_serialization.cpp`. README v0.1 table appended. --- Part of a structural refactor: move from per-entity `*Db` clients to a shared `Repository<T>` abstraction. This issue lands the **interfaces only** in oatpp-authkit. No behavior change yet, no concrete implementations, no callers updated. ## Scope Add to webapp-scaffold: - `Repository<TDto>` — pure abstract interface, **temporal-agnostic**: - `findByEntityId(entityId)` - `list(filter)` - `save(dto)` — generates a random UUID if `entity_id` is null on the DTO; otherwise uses the caller-supplied id (mixed allocation policy) - `softDelete(entityId)` - `ITemporalEntity` — DTO mixin/marker requiring `entity_id`, `valid_from`, `valid_until` - `IHistoryRepository<TDto>` — **separate** interface for `history(entityId)`, only implemented by repositories that wrap temporal entities - `TemporalAt` — value type: "live" or a specific point in time - `ActorContext` — placeholder for who is performing the action (used by the scope-guard decorator in the next issue) ## Out of scope - Any concrete repository implementations - Decorators (temporal, scope-guard) — separate issue - Transactions / unit-of-work — explicitly deferred - Any migration of controllers or `*Db` callers in fewo-webapp ## Acceptance - Interfaces compile in oatpp-authkit - A trivial in-memory test fake of `Repository<MockDto>` exists, demonstrating the contract without touching SQL ## Decisions (from planning discussion) 1. `entity_id` allocation: **mixed** — client passes id OR repository allocates a random UUID if null 2. UnitOfWork: **deferred**, not in scope 3. `Repository<T>` shape: **pure abstract interface** (not C++20 concept) 4. History queries: live on a **separate** `IHistoryRepository<T>`, not on `Repository<T>`
Author
Owner

Agent Evaluation

Feasibility: Easy. The host repo decision is settled (Option A → oatpp-authkit), and the issue body is explicit about layout (include/oatpp-authkit/repo/), test style (plain executable + REQUIRE matching test_negotiation.cpp), and the four design decisions (mixed UUID allocation, no UnitOfWork, abstract interface not concept, separate IHistoryRepository<T>). Nothing here requires further design discussion.

Impact: High and structural. This is the foundation header set the whole Repository<T> rollout sits on (oatpp-authkit #8 decorators, fewo-webapp #457 pilot, fewo-webapp #458 phases 4-7). The interfaces are also reusable across other oatpp-authkit consumers, fitting the library's "distilled from fewo-webapp's hardened stack" charter.

Effort: Small (~150 LOC headers + ~120 LOC test).

Recommendation: Accept.

Implementation plan

  1. Create include/oatpp-authkit/repo/ directory.
  2. Add headers (header-only, C++17, oatpp 1.3+):
    • Repository.hpptemplate <class TDto> class Repository { virtual ~Repository(); virtual oatpp::Object<TDto> findByEntityId(const oatpp::String&) = 0; virtual oatpp::Vector<oatpp::Object<TDto>> list(...) = 0; virtual void save(oatpp::Object<TDto>) = 0; virtual void softDelete(const oatpp::String&) = 0; };save semantics: if dto->entity_id is null, generate a UUID v4; otherwise use the supplied value.
    • ITemporalEntity.hppstruct ITemporalEntity { static_assert(...) } or a documentation comment + concept-style requires check at decorator site (since the decision was abstract interface, plain inheritance is fine).
    • IHistoryRepository.hpptemplate <class TDto> class IHistoryRepository { virtual oatpp::Vector<oatpp::Object<TDto>> history(const oatpp::String&) = 0; };
    • TemporalAt.hppstruct TemporalAt { enum Kind { Live, At } kind; v_int64 timestamp; static TemporalAt live(); static TemporalAt at(v_int64); };
    • ActorContext.hpp — minimal placeholder struct ActorContext { oatpp::String userId; oatpp::Vector<oatpp::String> allowedScopes; }; — extend in the scope-guard decorator issue if needed.
  3. Add test/test_repository_interface.cpp:
    • Define a tiny MockDto (entity_id + name).
    • Implement InMemoryRepo : Repository<MockDto> using a std::unordered_map.
    • REQUIRE: save(null-id) allocates UUID; save(supplied-id) reuses it; findByEntityId round-trips; softDelete removes.
  4. Wire in test/CMakeLists.txt: add_executable(test_repository_interface ...), add_test(NAME repository_interface ...).
  5. README: append a row to the v0.1 table for the new repo/ headers.
  6. Build check: cmake -B build -DOATPP_AUTHKIT_BUILD_TESTS=ON && cmake --build build && ctest --test-dir build clean.

Decision needed

None — design decisions all settled in the issue body and prior cross-repo discussion. Owner can add accepted to unblock implementation.

## Agent Evaluation **Feasibility:** Easy. The host repo decision is settled (Option A → oatpp-authkit), and the issue body is explicit about layout (`include/oatpp-authkit/repo/`), test style (plain executable + REQUIRE matching `test_negotiation.cpp`), and the four design decisions (mixed UUID allocation, no UnitOfWork, abstract interface not concept, separate `IHistoryRepository<T>`). Nothing here requires further design discussion. **Impact:** High and structural. This is the foundation header set the whole `Repository<T>` rollout sits on (oatpp-authkit #8 decorators, fewo-webapp #457 pilot, fewo-webapp #458 phases 4-7). The interfaces are also reusable across other oatpp-authkit consumers, fitting the library's "distilled from fewo-webapp's hardened stack" charter. **Effort:** Small (~150 LOC headers + ~120 LOC test). **Recommendation:** Accept. ### Implementation plan 1. Create `include/oatpp-authkit/repo/` directory. 2. Add headers (header-only, C++17, oatpp 1.3+): - `Repository.hpp` — `template <class TDto> class Repository { virtual ~Repository(); virtual oatpp::Object<TDto> findByEntityId(const oatpp::String&) = 0; virtual oatpp::Vector<oatpp::Object<TDto>> list(...) = 0; virtual void save(oatpp::Object<TDto>) = 0; virtual void softDelete(const oatpp::String&) = 0; };` — `save` semantics: if `dto->entity_id` is null, generate a UUID v4; otherwise use the supplied value. - `ITemporalEntity.hpp` — `struct ITemporalEntity { static_assert(...) }` or a documentation comment + concept-style `requires` check at decorator site (since the decision was abstract interface, plain inheritance is fine). - `IHistoryRepository.hpp` — `template <class TDto> class IHistoryRepository { virtual oatpp::Vector<oatpp::Object<TDto>> history(const oatpp::String&) = 0; };` - `TemporalAt.hpp` — `struct TemporalAt { enum Kind { Live, At } kind; v_int64 timestamp; static TemporalAt live(); static TemporalAt at(v_int64); };` - `ActorContext.hpp` — minimal placeholder `struct ActorContext { oatpp::String userId; oatpp::Vector<oatpp::String> allowedScopes; };` — extend in the scope-guard decorator issue if needed. 3. Add `test/test_repository_interface.cpp`: - Define a tiny `MockDto` (entity_id + name). - Implement `InMemoryRepo : Repository<MockDto>` using a `std::unordered_map`. - REQUIRE: `save(null-id)` allocates UUID; `save(supplied-id)` reuses it; `findByEntityId` round-trips; `softDelete` removes. 4. Wire in `test/CMakeLists.txt`: `add_executable(test_repository_interface ...)`, `add_test(NAME repository_interface ...)`. 5. README: append a row to the v0.1 table for the new `repo/` headers. 6. Build check: `cmake -B build -DOATPP_AUTHKIT_BUILD_TESTS=ON && cmake --build build && ctest --test-dir build` clean. ### Decision needed None — design decisions all settled in the issue body and prior cross-repo discussion. Owner can add `accepted` to unblock implementation.
uwe.admin added the
effort:medium
evaluated
labels 2026-04-27 21:31:25 +02:00
Author
Owner

Evaluation carried over from closed uwe.admin/webapp-scaffold#8; the migration comment at the top of this issue body summarises the prior eval. No additional design questions remain.

Evaluation carried over from closed uwe.admin/webapp-scaffold#8; the migration comment at the top of this issue body summarises the prior eval. No additional design questions remain.
Author
Owner

Re-evaluation

Acknowledged — owner's note confirms the evaluation carries over from the closed scaffold ticket and no new design questions remain. Original eval (Small effort, Accept recommendation, plan in this thread) stands as-is. Ready for the accepted label to unblock implementation.

## Re-evaluation Acknowledged — owner's note confirms the evaluation carries over from the closed scaffold ticket and no new design questions remain. Original eval (Small effort, Accept recommendation, plan in this thread) stands as-is. Ready for the `accepted` label to unblock implementation.
u.schuster added the
accepted
label 2026-04-27 22:38:33 +02:00
Author
Owner

Implemented #7 → commit a0c61b3 — 5 headers under include/oatpp-authkit/repo/, in-memory test fake covering mixed-id allocation / find / list / softDelete / TemporalAt / ActorContext / ITemporalEntity marker; all 5 ctest targets pass; README updated. Unblocks decorator work in #8 and the fewo-webapp pilot at uwe.admin/fewo-webapp#457.

Implemented #7 → commit a0c61b3 — 5 headers under `include/oatpp-authkit/repo/`, in-memory test fake covering mixed-id allocation / find / list / softDelete / TemporalAt / ActorContext / ITemporalEntity marker; all 5 ctest targets pass; README updated. Unblocks decorator work in #8 and the fewo-webapp pilot at uwe.admin/fewo-webapp#457.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: uwe.admin/oatpp-authkit#7
No description provided.