Current state (as-is)
From the current codebase:
- Capability interfaces are already present under
docraft/include/docraft/backend/ (e.g. IDocraftLineRenderingBackend in docraft/include/docraft/backend/docraft_line_rendering_backend.h).
- Concrete backend implementation is currently centered on Haru/PDF in
docraft/src/docraft/backend/pdf/docraft_haru_backend.cc.
- Backend responsibilities are still effectively concentrated in the concrete backend layer, and interface relationships/coupling should be simplified.
This issue proposes an incremental refactor from the current shape to a composition-first architecture.
Why
The current backend structure makes capability evolution harder than necessary:
- capability concerns are not isolated enough at implementation level,
- changes in one area can affect unrelated rendering code,
- testing individual capabilities is harder,
- future backends would inherit current coupling patterns.
Target architecture
- Keep capability interfaces independent (no inheritance chain between capability interfaces).
- Decompose the Haru backend implementation into smaller capability-focused components.
- Expose capability access from the backend root via:
- immutable:
<capability>() const
- mutable:
edit_<capability>()
- Migrate existing call sites to use capability accessors explicitly.
Accessor conventions (project style)
Use no get_ prefix for immutable accessors.
Naming
- Immutable:
line_rendering() const
- Mutable:
edit_line_rendering()
- Apply same convention to all capabilities.
Return types
If capability is guaranteed:
const IDocraftLineRenderingBackend& line_rendering() const;
IDocraftLineRenderingBackend& edit_line_rendering();
If capability can be absent:
const IDocraftLineRenderingBackend* line_rendering() const;
IDocraftLineRenderingBackend* edit_line_rendering();
Pick one availability policy and apply it consistently.
Proposed incremental scope (aligned to current repo)
docraft/include/docraft/backend/
- ensure each capability interface is standalone and chain-free.
docraft/src/docraft/backend/pdf/docraft_haru_backend.cc
- split monolithic responsibilities into composed capability objects (line/text/shapes/etc.).
- backend root interface/type (where defined)
- add/standardize
<capability>() const + edit_<capability>().
- dependent renderer/pipeline code
- update usage to explicit capability access.
- tests/docs
- add coverage for const/mutable accessor behavior and update API docs.
Tasks
Acceptance criteria
- Capability interfaces under
docraft/include/docraft/backend/ are independent (no chain inheritance).
- Haru backend implementation is composed of smaller capability-oriented parts instead of a single concentrated responsibility center.
- Backend root exposes consistent accessor pairs without
get_ prefix.
- Existing rendering behavior remains unchanged (regression tests pass).
- Documentation reflects the new backend composition/access pattern.
Risk and rollout notes
- This is a cross-cutting internal API refactor; prefer staged migration.
- If needed, keep temporary adapter methods during transition and remove them in a follow-up cleanup issue.
- Prioritize line-rendering capability first (already explicit via
IDocraftLineRenderingBackend) as migration template for other capabilities.
Clarification: “smaller objects” means internal capability components
In this refactor, “smaller objects” does not mean introducing multiple standalone backends.
It means splitting the current Haru backend implementation into focused internal components, each implementing one capability interface.
Example direction:
HaruLineRenderingBackend implements IDocraftLineRenderingBackend
HaruTextRenderingBackend implements text capability interface
HaruShapeRenderingBackend implements shape capability interface
The backend root remains the single integration point, but acts as a thin composition/dispatch layer exposing:
<capability>() const
edit_<capability>()
This keeps responsibilities isolated while preserving one concrete backend entry point.
Current state (as-is)
From the current codebase:
docraft/include/docraft/backend/(e.g.IDocraftLineRenderingBackendindocraft/include/docraft/backend/docraft_line_rendering_backend.h).docraft/src/docraft/backend/pdf/docraft_haru_backend.cc.This issue proposes an incremental refactor from the current shape to a composition-first architecture.
Why
The current backend structure makes capability evolution harder than necessary:
Target architecture
<capability>() constedit_<capability>()Accessor conventions (project style)
Use no
get_prefix for immutable accessors.Naming
line_rendering() constedit_line_rendering()Return types
If capability is guaranteed:
const IDocraftLineRenderingBackend& line_rendering() const;IDocraftLineRenderingBackend& edit_line_rendering();If capability can be absent:
const IDocraftLineRenderingBackend* line_rendering() const;IDocraftLineRenderingBackend* edit_line_rendering();Pick one availability policy and apply it consistently.
Proposed incremental scope (aligned to current repo)
docraft/include/docraft/backend/docraft/src/docraft/backend/pdf/docraft_haru_backend.cc<capability>() const+edit_<capability>().Tasks
docraft/include/docraft/backend/and remove capability inheritance chaining.docraft/src/docraft/backend/pdf/docraft_haru_backend.cc.<capability>() constedit_<capability>()Acceptance criteria
docraft/include/docraft/backend/are independent (no chain inheritance).get_prefix.Risk and rollout notes
IDocraftLineRenderingBackend) as migration template for other capabilities.Clarification: “smaller objects” means internal capability components
In this refactor, “smaller objects” does not mean introducing multiple standalone backends.
It means splitting the current Haru backend implementation into focused internal components, each implementing one capability interface.
Example direction:
HaruLineRenderingBackendimplementsIDocraftLineRenderingBackendHaruTextRenderingBackendimplements text capability interfaceHaruShapeRenderingBackendimplements shape capability interfaceThe backend root remains the single integration point, but acts as a thin composition/dispatch layer exposing:
<capability>() constedit_<capability>()This keeps responsibilities isolated while preserving one concrete backend entry point.