mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
docs(architecture): add Architecture Decision Records (ADRs) documentation
Introduce a comprehensive guide for creating and maintaining ADRs within the ISA-Frontend project. This includes an overview, structure, naming conventions, and process guidelines to ensure consistent documentation of architectural decisions.
This commit is contained in:
200
docs/architecture/README.md
Normal file
200
docs/architecture/README.md
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
# Architecture Decision Records (ADRs)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Architecture Decision Records (ADRs) are lightweight documents that capture important architectural decisions made during the development of the ISA-Frontend project. They provide context for why certain decisions were made, helping current and future team members understand the reasoning behind architectural choices.
|
||||||
|
|
||||||
|
## What are ADRs?
|
||||||
|
|
||||||
|
An Architecture Decision Record is a document that captures a single architectural decision and its rationale. The goal of an ADR is to document the architectural decisions that are being made so that:
|
||||||
|
|
||||||
|
- **Future team members** can understand why certain decisions were made
|
||||||
|
- **Current team members** can refer back to the reasoning behind decisions
|
||||||
|
- **Architectural evolution** can be tracked over time
|
||||||
|
- **Knowledge transfer** is facilitated during team changes
|
||||||
|
|
||||||
|
## ADR Structure
|
||||||
|
|
||||||
|
Each ADR follows a consistent structure based on our [TEMPLATE.md](./TEMPLATE.md) and includes:
|
||||||
|
|
||||||
|
- **Problem Statement**: What architectural challenge needs to be addressed
|
||||||
|
- **Decision**: The architectural decision made
|
||||||
|
- **Rationale**: Why this decision was chosen
|
||||||
|
- **Consequences**: Both positive and negative outcomes of the decision
|
||||||
|
- **Alternatives**: Other options that were considered
|
||||||
|
- **Implementation**: Technical details and examples
|
||||||
|
- **Status**: Current state of the decision (Draft, Approved, Superseded, etc.)
|
||||||
|
|
||||||
|
## Naming Convention
|
||||||
|
|
||||||
|
ADRs should follow this naming pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
NNNN-short-descriptive-title.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Where:
|
||||||
|
- `NNNN` is a 4-digit sequential number (e.g., 0001, 0002, 0003...)
|
||||||
|
- `short-descriptive-title` uses kebab-case and briefly describes the decision
|
||||||
|
- `.md` indicates it's a Markdown file
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
- `0001-use-standalone-components.md`
|
||||||
|
- `0002-adopt-ngrx-signals.md`
|
||||||
|
- `0003-implement-micro-frontend-architecture.md`
|
||||||
|
- `0004-choose-vitest-over-jest.md`
|
||||||
|
|
||||||
|
## Process Guidelines
|
||||||
|
|
||||||
|
### 1. When to Create an ADR
|
||||||
|
|
||||||
|
Create an ADR when making decisions about:
|
||||||
|
|
||||||
|
- **Architecture patterns** (e.g., micro-frontends, monorepo structure)
|
||||||
|
- **Technology choices** (e.g., testing frameworks, state management)
|
||||||
|
- **Development practices** (e.g., code organization, build processes)
|
||||||
|
- **Technical standards** (e.g., coding conventions, performance requirements)
|
||||||
|
- **Infrastructure decisions** (e.g., deployment strategies, CI/CD processes)
|
||||||
|
|
||||||
|
### 2. ADR Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
Draft → Under Review → Approved → [Superseded/Deprecated]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Draft**: Initial version, being written
|
||||||
|
- **Under Review**: Shared with team for feedback and discussion
|
||||||
|
- **Approved**: Team has agreed and decision is implemented
|
||||||
|
- **Superseded**: Replaced by a newer ADR
|
||||||
|
- **Deprecated**: No longer applicable but kept for historical reference
|
||||||
|
|
||||||
|
### 3. Creation Process
|
||||||
|
|
||||||
|
1. **Identify the Need**: Recognize an architectural decision needs documentation
|
||||||
|
2. **Create from Template**: Copy [TEMPLATE.md](./TEMPLATE.md) to create new ADR
|
||||||
|
3. **Fill in Content**: Complete all sections with relevant information
|
||||||
|
4. **Set Status to Draft**: Mark the document as "Draft" initially
|
||||||
|
5. **Share for Review**: Present to team for discussion and feedback
|
||||||
|
6. **Iterate**: Update based on team input
|
||||||
|
7. **Approve**: Once consensus is reached, mark as "Approved"
|
||||||
|
8. **Implement**: Begin implementation of the architectural decision
|
||||||
|
|
||||||
|
### 4. Review Process
|
||||||
|
|
||||||
|
- **Author Review**: Self-review for completeness and clarity
|
||||||
|
- **Peer Review**: Share with relevant team members for technical review
|
||||||
|
- **Architecture Review**: Present in architecture meetings if significant
|
||||||
|
- **Final Approval**: Get sign-off from technical leads/architects
|
||||||
|
|
||||||
|
## Angular/Nx Specific Considerations
|
||||||
|
|
||||||
|
When writing ADRs for this project, consider these Angular/Nx specific aspects:
|
||||||
|
|
||||||
|
### Architecture Decisions
|
||||||
|
- **Library organization** in the monorepo structure
|
||||||
|
- **Dependency management** between applications and libraries
|
||||||
|
- **Feature module vs. standalone component** approaches
|
||||||
|
- **State management patterns** (NgRx, Signals, Services)
|
||||||
|
- **Routing strategies** for large applications
|
||||||
|
|
||||||
|
### Technical Decisions
|
||||||
|
- **Build optimization** strategies using Nx
|
||||||
|
- **Testing approaches** for different types of libraries
|
||||||
|
- **Code sharing patterns** across applications
|
||||||
|
- **Performance optimization** techniques
|
||||||
|
- **Bundle splitting** and lazy loading strategies
|
||||||
|
|
||||||
|
### Development Workflow
|
||||||
|
- **Nx executor usage** for custom tasks
|
||||||
|
- **Generator patterns** for code scaffolding
|
||||||
|
- **Linting and formatting** configurations
|
||||||
|
- **CI/CD pipeline** optimizations using Nx affected commands
|
||||||
|
|
||||||
|
## Template Usage
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
|
1. Copy the [TEMPLATE.md](./TEMPLATE.md) file
|
||||||
|
2. Rename it following the naming convention
|
||||||
|
3. Replace placeholder text with actual content
|
||||||
|
4. Focus on the "why" not just the "what"
|
||||||
|
5. Include concrete examples and code snippets
|
||||||
|
6. Consider both immediate and long-term consequences
|
||||||
|
|
||||||
|
### Key Template Sections
|
||||||
|
|
||||||
|
- **Decision**: State the architectural decision clearly and concisely
|
||||||
|
- **Context**: Provide background information and constraints
|
||||||
|
- **Consequences**: Be honest about both benefits and drawbacks
|
||||||
|
- **Implementation**: Include practical examples relevant to Angular/Nx
|
||||||
|
- **Alternatives**: Show you considered other options
|
||||||
|
|
||||||
|
## Examples of Good ADRs
|
||||||
|
|
||||||
|
Here are some example titles that would make good ADRs for this project:
|
||||||
|
|
||||||
|
- **State Management**: "0001-adopt-ngrx-signals-for-component-state.md"
|
||||||
|
- **Testing Strategy**: "0002-use-angular-testing-utilities-over-spectator.md"
|
||||||
|
- **Code Organization**: "0003-implement-domain-driven-library-structure.md"
|
||||||
|
- **Performance**: "0004-implement-lazy-loading-for-feature-modules.md"
|
||||||
|
- **Build Process**: "0005-use-nx-cloud-for-distributed-task-execution.md"
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Writing Effective ADRs
|
||||||
|
|
||||||
|
1. **Be Concise**: Keep it focused and to the point
|
||||||
|
2. **Be Specific**: Include concrete examples and implementation details
|
||||||
|
3. **Be Honest**: Document both pros and cons honestly
|
||||||
|
4. **Be Timely**: Write ADRs close to when decisions are made
|
||||||
|
5. **Be Collaborative**: Involve relevant team members in the process
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
|
||||||
|
- **Review Regularly**: Check ADRs during architecture reviews
|
||||||
|
- **Update Status**: Keep status current as decisions evolve
|
||||||
|
- **Link Related ADRs**: Reference connected decisions
|
||||||
|
- **Archive Outdated**: Mark superseded ADRs appropriately
|
||||||
|
|
||||||
|
### Code Examples
|
||||||
|
|
||||||
|
When including code examples:
|
||||||
|
- Use actual project syntax and patterns
|
||||||
|
- Include both TypeScript and template examples where relevant
|
||||||
|
- Show before/after scenarios for changes
|
||||||
|
- Reference specific files in the codebase when possible
|
||||||
|
|
||||||
|
## Tools and Integration
|
||||||
|
|
||||||
|
### Recommended Tools
|
||||||
|
|
||||||
|
- **Markdown Editor**: Use any markdown-capable editor
|
||||||
|
- **Version Control**: All ADRs are tracked in Git
|
||||||
|
- **Review Process**: Use PR reviews for ADR approval
|
||||||
|
- **Documentation**: Link ADRs from relevant code comments
|
||||||
|
|
||||||
|
### Integration with Development
|
||||||
|
|
||||||
|
- Reference ADR numbers in commit messages when implementing decisions
|
||||||
|
- Include ADR links in PR descriptions for architectural changes
|
||||||
|
- Update ADRs when decisions need modification
|
||||||
|
- Use ADRs as reference during code reviews
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
### Questions or Issues?
|
||||||
|
|
||||||
|
- **Team Discussions**: Bring up in team meetings or Slack
|
||||||
|
- **Architecture Review**: Present in architecture meetings
|
||||||
|
- **Documentation**: Update this README if process improvements are needed
|
||||||
|
|
||||||
|
### Resources
|
||||||
|
|
||||||
|
- [Architecture Decision Records (ADRs) - Michael Nygard](https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions)
|
||||||
|
- [ADR GitHub Organization](https://adr.github.io/)
|
||||||
|
- [Nx Documentation](https://nx.dev/getting-started/intro)
|
||||||
|
- [Angular Architecture Guide](https://angular.dev/guide/architecture)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This ADR system helps maintain architectural consistency and knowledge sharing across the ISA-Frontend project. Keep it updated and use it regularly for the best results.*
|
||||||
138
docs/architecture/TEMPLATE.md
Normal file
138
docs/architecture/TEMPLATE.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# ADR NNNN: <short-descriptive-title>
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Status | Draft / Under Review / Approved / Superseded by ADR NNNN / Deprecated |
|
||||||
|
| Date | YYYY-MM-DD |
|
||||||
|
| Owners | <author(s)> |
|
||||||
|
| Participants | <key reviewers / stakeholders> |
|
||||||
|
| Related ADRs | NNNN (title), NNNN (title) |
|
||||||
|
| Tags | architecture, <domain>, <category> |
|
||||||
|
|
||||||
|
---
|
||||||
|
## Summary (Decision in One Sentence)
|
||||||
|
Concise statement of the architectural decision. Avoid rationale here—just the what.
|
||||||
|
|
||||||
|
## Context & Problem Statement
|
||||||
|
Describe the background and the problem this decision addresses.
|
||||||
|
- Business drivers / user needs
|
||||||
|
- Technical constraints (performance, security, scalability, compliance, legacy, regulatory)
|
||||||
|
- Current pain points / gaps
|
||||||
|
- Measurable goals / success criteria (e.g. reduce build time by 30%)
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
What is in scope and explicitly out of scope for this decision.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
State the decision clearly (active voice). Include high-level approach or pattern selection, not implementation detail.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
Why this option was selected:
|
||||||
|
- Alignment with strategic/technical direction
|
||||||
|
- Trade-offs considered
|
||||||
|
- Data, benchmarks, experiments, spikes
|
||||||
|
- Impact on developer experience / velocity
|
||||||
|
- Long-term maintainability & extensibility
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
| Alternative | Summary | Pros | Cons | Reason Not Chosen |
|
||||||
|
|-------------|---------|------|------|-------------------|
|
||||||
|
| Option A | | | | |
|
||||||
|
| Option B | | | | |
|
||||||
|
| Option C | | | | |
|
||||||
|
|
||||||
|
Add deeper detail below if needed:
|
||||||
|
### Option A – <name>
|
||||||
|
### Option B – <name>
|
||||||
|
### Option C – <name>
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
### Positive
|
||||||
|
- …
|
||||||
|
### Negative / Risks / Debt Introduced
|
||||||
|
- …
|
||||||
|
### Neutral / Open Questions
|
||||||
|
- …
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
High-level rollout strategy. Break into phases if applicable.
|
||||||
|
1. Phase 0 – Spike / Validation
|
||||||
|
2. Phase 1 – Foundation / Infrastructure
|
||||||
|
3. Phase 2 – Incremental Adoption / Migration
|
||||||
|
4. Phase 3 – Hardening / Optimization
|
||||||
|
5. Phase 4 – Decommission Legacy
|
||||||
|
|
||||||
|
### Tasks / Workstreams
|
||||||
|
- Infra / tooling changes
|
||||||
|
- Library additions (Nx generators, new libs under `libs/<domain>`)
|
||||||
|
- Refactors / migrations
|
||||||
|
- Testing strategy updates (Jest → Vitest, Signals adoption, etc.)
|
||||||
|
- Documentation & onboarding materials
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
List objective criteria to mark implementation complete.
|
||||||
|
|
||||||
|
### Rollback Plan
|
||||||
|
How to revert safely if outcomes are negative.
|
||||||
|
|
||||||
|
## Architectural Impact
|
||||||
|
### Nx / Monorepo Layout
|
||||||
|
Describe changes to library boundaries, tags, dependency graph, affected projects.
|
||||||
|
### Module / Library Design
|
||||||
|
New or modified public APIs (`src/index.ts` changes, path aliases additions to `tsconfig.base.json`).
|
||||||
|
### State Management
|
||||||
|
Implications for Signals, NgRx, resource factories, persistence patterns (`withStorage`).
|
||||||
|
### Runtime & Performance
|
||||||
|
Bundle size, lazy loading, code splitting, caching, SSR/hydration considerations.
|
||||||
|
### Security & Compliance
|
||||||
|
AuthZ/AuthN, token handling, data residency, PII, secure storage.
|
||||||
|
### Observability & Logging
|
||||||
|
Logging contexts (`@isa/core/logging`), metrics, tracing hooks.
|
||||||
|
### DX / Tooling
|
||||||
|
Generators, lint rules, schematic updates, local dev flow.
|
||||||
|
|
||||||
|
## Detailed Design Elements
|
||||||
|
(Optional deeper technical articulation.)
|
||||||
|
- Sequence diagrams / component diagrams
|
||||||
|
- Data flow / async flow
|
||||||
|
- Error handling strategy
|
||||||
|
- Concurrency / cancellation (e.g. `rxMethod` + `switchMap` usage)
|
||||||
|
- Abstractions & extension points
|
||||||
|
|
||||||
|
## Code Examples
|
||||||
|
### Before
|
||||||
|
```ts
|
||||||
|
// Previous approach (simplified)
|
||||||
|
```
|
||||||
|
### After
|
||||||
|
```ts
|
||||||
|
// New approach (simplified)
|
||||||
|
```
|
||||||
|
### Migration Snippet
|
||||||
|
```ts
|
||||||
|
// Example incremental migration pattern
|
||||||
|
```
|
||||||
|
|
||||||
|
## Open Questions / Follow-Ups
|
||||||
|
- Unresolved design clarifications
|
||||||
|
- Dependent ADRs required
|
||||||
|
- External approvals needed
|
||||||
|
|
||||||
|
## Decision Review & Revalidation
|
||||||
|
When and how this ADR will be re-evaluated (date, trigger conditions, metrics thresholds).
|
||||||
|
|
||||||
|
## Status Log
|
||||||
|
| Date | Change | Author |
|
||||||
|
|------|--------|--------|
|
||||||
|
| YYYY-MM-DD | Created (Draft) | |
|
||||||
|
| YYYY-MM-DD | Approved | |
|
||||||
|
| YYYY-MM-DD | Superseded by ADR NNNN | |
|
||||||
|
|
||||||
|
## References
|
||||||
|
- Links to spike notes, benchmark results
|
||||||
|
- External articles, standards, RFCs
|
||||||
|
- Related code PRs / commits
|
||||||
|
|
||||||
|
---
|
||||||
|
> Document updates MUST reference this ADR number in commit messages: `ADR-NNNN:` prefix.
|
||||||
|
> Keep this document updated through all lifecycle stages.
|
||||||
506
docs/architecture/adr/0001-implement-data-access-api-requests.md
Normal file
506
docs/architecture/adr/0001-implement-data-access-api-requests.md
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
# ADR 0001: Implement `data-access` API Requests
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Status | Draft |
|
||||||
|
| Date | 29.09.2025 |
|
||||||
|
| Owners | Lorenz, Nino |
|
||||||
|
| Participants | N/A |
|
||||||
|
| Related ADRs | N/A |
|
||||||
|
| Tags | architecture, data-access, library, swagger |
|
||||||
|
|
||||||
|
---
|
||||||
|
## Summary (Decision in One Sentence)
|
||||||
|
Standardize data-access library implementation patterns for API requests using Zod schemas for validation, domain-specific models extending generated DTOs, and service layers that integrate with generated Swagger clients.
|
||||||
|
|
||||||
|
## Context & Problem Statement
|
||||||
|
The ISA Frontend application requires consistent and maintainable patterns for implementing API requests across multiple domain libraries. Current data-access libraries show varying implementation approaches that need standardization.
|
||||||
|
|
||||||
|
**Business drivers / user needs:**
|
||||||
|
- Consistent error handling across all API interactions
|
||||||
|
- Type-safe request/response handling to prevent runtime errors
|
||||||
|
- Maintainable code structure for easy onboarding and development
|
||||||
|
- Reliable validation of API inputs and outputs
|
||||||
|
|
||||||
|
**Technical constraints:**
|
||||||
|
- Must integrate with generated Swagger clients (`@generated/swagger/*`)
|
||||||
|
- Need to support abort signals for request cancellation
|
||||||
|
- Require caching and performance optimization capabilities
|
||||||
|
- Must align with existing logging infrastructure (`@isa/core/logging`)
|
||||||
|
- Support for domain-specific model extensions beyond generated DTOs
|
||||||
|
|
||||||
|
**Current pain points:**
|
||||||
|
- Inconsistent validation patterns across different data-access libraries
|
||||||
|
- Mixed approaches to error handling and response processing
|
||||||
|
- Duplication of common patterns (abort signal handling, response parsing)
|
||||||
|
- Lack of standardized model extension patterns
|
||||||
|
|
||||||
|
**Measurable goals:**
|
||||||
|
- Standardize API request patterns across all 4+ data-access libraries
|
||||||
|
- Reduce boilerplate code by 40% through shared utilities
|
||||||
|
- Improve type safety with comprehensive Zod schema coverage
|
||||||
|
|
||||||
|
### Scope
|
||||||
|
**In scope:**
|
||||||
|
- Schema validation patterns using Zod
|
||||||
|
- Model definition standards extending generated DTOs
|
||||||
|
- Service implementation patterns with generated Swagger clients
|
||||||
|
- Error handling and response processing standardization
|
||||||
|
- Integration with common utilities and logging
|
||||||
|
|
||||||
|
**Out of scope:**
|
||||||
|
- Modification of generated Swagger client code
|
||||||
|
- Changes to backend API contracts
|
||||||
|
- Authentication/authorization mechanisms
|
||||||
|
- Caching implementation details (handled by decorators)
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
Implement a three-layer architecture pattern for data-access libraries:
|
||||||
|
|
||||||
|
1. **Schema Layer**: Use Zod schemas for input validation and type inference, following the naming convention `<Operation>Schema` with corresponding `<Operation>` and `<Operation>Input` types
|
||||||
|
2. **Model Layer**: Define domain-specific interfaces that extend generated DTOs, using `EntityContainer<T>` pattern for lazy-loaded relationships
|
||||||
|
3. **Service Layer**: Create injectable services that integrate generated Swagger clients, implement standardized error handling, and support request cancellation via AbortSignal
|
||||||
|
|
||||||
|
All data-access libraries will follow the standard export structure: `models`, `schemas`, `services`, and optionally `stores` and `helpers`.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
**Alignment with strategic/technical direction:**
|
||||||
|
- Leverages existing Zod integration for consistent validation across the application
|
||||||
|
- Builds upon established generated Swagger client infrastructure
|
||||||
|
- Aligns with Angular dependency injection patterns and service architecture
|
||||||
|
- Supports the project's type-safety goals with TypeScript
|
||||||
|
|
||||||
|
**Trade-offs considered:**
|
||||||
|
- **Schema validation overhead**: Zod validation adds minimal runtime cost but provides significant development-time safety
|
||||||
|
- **Model extension complexity**: Interface extension pattern adds a layer but enables domain-specific enhancements
|
||||||
|
- **Service layer abstraction**: Additional abstraction over generated clients but enables consistent error handling and logging
|
||||||
|
|
||||||
|
**Evidence from current implementation:**
|
||||||
|
- Analysis of 4 data-access libraries shows successful patterns in `catalogue`, `remission`, `crm`, and `oms`
|
||||||
|
- `RemissionReturnReceiptService` demonstrates effective integration with logging and error handling
|
||||||
|
- `EntityContainer<T>` pattern proven effective for lazy-loaded relationships in remission domain
|
||||||
|
|
||||||
|
**Developer experience impact:**
|
||||||
|
- Consistent patterns reduce cognitive load when switching between domains
|
||||||
|
- Type inference from Zod schemas eliminates manual type definitions
|
||||||
|
- Standardized error handling reduces debugging time
|
||||||
|
- Auto-completion and type safety improve development velocity
|
||||||
|
|
||||||
|
**Long-term maintainability:**
|
||||||
|
- Clear separation of concerns between validation, models, and API integration
|
||||||
|
- Generated client changes don't break domain-specific model extensions
|
||||||
|
- Consistent logging and error handling simplifies troubleshooting
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
| Alternative | Summary | Pros | Cons | Reason Not Chosen |
|
||||||
|
|-------------|---------|------|------|-------------------|
|
||||||
|
| Option A | | | | |
|
||||||
|
| Option B | | | | |
|
||||||
|
| Option C | | | | |
|
||||||
|
|
||||||
|
Add deeper detail below if needed:
|
||||||
|
### Option A – <name>
|
||||||
|
### Option B – <name>
|
||||||
|
### Option C – <name>
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
### Positive
|
||||||
|
- …
|
||||||
|
### Negative / Risks / Debt Introduced
|
||||||
|
- …
|
||||||
|
### Neutral / Open Questions
|
||||||
|
- …
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Phase 0 – Analysis & Standards (Completed)
|
||||||
|
- ✅ Analyzed existing data-access libraries (`catalogue`, `remission`, `crm`, `oms`)
|
||||||
|
- ✅ Identified common patterns and best practices
|
||||||
|
- ✅ Documented standard library structure
|
||||||
|
|
||||||
|
### Phase 1 – Common Utilities Enhancement
|
||||||
|
- Enhance `@isa/common/data-access` with additional utilities
|
||||||
|
- Add standardized error types and response handling
|
||||||
|
- Create reusable operators and decorators
|
||||||
|
- Add helper functions for common API patterns
|
||||||
|
|
||||||
|
### Phase 2 – Template & Generator Creation
|
||||||
|
- Create Nx generator for new data-access libraries
|
||||||
|
- Develop template files for schemas, models, and services
|
||||||
|
- Add code snippets and documentation templates
|
||||||
|
- Create migration guide for existing libraries
|
||||||
|
|
||||||
|
### Phase 3 – Existing Library Standardization
|
||||||
|
- Update `catalogue/data-access` to follow complete pattern
|
||||||
|
- Migrate `crm/data-access` to standard structure
|
||||||
|
- Ensure `remission/data-access` follows all conventions
|
||||||
|
- Standardize `oms/data-access` implementation
|
||||||
|
|
||||||
|
### Phase 4 – New Library Implementation
|
||||||
|
- Apply patterns to new domain libraries as they're created
|
||||||
|
- Use Nx generator for consistent setup
|
||||||
|
- Enforce patterns through code review and linting
|
||||||
|
|
||||||
|
### Tasks / Workstreams
|
||||||
|
**Infrastructure:**
|
||||||
|
- Update `@isa/common/data-access` with enhanced utilities
|
||||||
|
- Add ESLint rules for data-access pattern enforcement
|
||||||
|
- Update `tsconfig.base.json` path mappings as needed
|
||||||
|
|
||||||
|
**Library Enhancements:**
|
||||||
|
- Create Nx generator: `nx g @isa/generators:data-access-lib <domain>`
|
||||||
|
- Add utility functions to `@isa/common/data-access`
|
||||||
|
- Enhanced error handling and logging patterns
|
||||||
|
|
||||||
|
**Migration Tasks:**
|
||||||
|
- Standardize schema validation across all libraries
|
||||||
|
- Ensure consistent model extension patterns
|
||||||
|
- Align service implementations with logging standards
|
||||||
|
- Update tests to match new patterns
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- Create data-access implementation guide
|
||||||
|
- Update onboarding materials with patterns
|
||||||
|
- Add code examples to development wiki
|
||||||
|
- Document generator usage and options
|
||||||
|
|
||||||
|
### Acceptance Criteria
|
||||||
|
- [ ] All data-access libraries follow standardized structure
|
||||||
|
- [ ] All API requests use Zod schema validation
|
||||||
|
- [ ] All services implement consistent error handling
|
||||||
|
- [ ] All services support AbortSignal for cancellation
|
||||||
|
- [ ] All models extend generated DTOs appropriately
|
||||||
|
- [ ] Nx generator produces compliant library structure
|
||||||
|
- [ ] Code review checklist includes data-access patterns
|
||||||
|
- [ ] Performance benchmarks show no degradation
|
||||||
|
|
||||||
|
### Rollback Plan
|
||||||
|
- Individual library changes can be reverted via Git
|
||||||
|
- Generated libraries can be recreated with previous patterns
|
||||||
|
- No breaking changes to existing public APIs
|
||||||
|
- Gradual migration allows for partial rollback by domain
|
||||||
|
|
||||||
|
## Architectural Impact
|
||||||
|
### Nx / Monorepo Layout
|
||||||
|
- Data-access libraries follow domain-based organization: `libs/<domain>/data-access/`
|
||||||
|
- Each library exports standard modules: `models`, `schemas`, `services`
|
||||||
|
- Dependencies on `@isa/common/data-access` for shared utilities
|
||||||
|
- Integration with generated Swagger clients via `@generated/swagger/<api-name>`
|
||||||
|
|
||||||
|
### Module / Library Design
|
||||||
|
**Standard public API structure (`src/index.ts`):**
|
||||||
|
```typescript
|
||||||
|
export * from './lib/models';
|
||||||
|
export * from './lib/schemas';
|
||||||
|
export * from './lib/services';
|
||||||
|
// Optional: stores, helpers
|
||||||
|
```
|
||||||
|
|
||||||
|
**Path aliases in `tsconfig.base.json`:**
|
||||||
|
- `@isa/<domain>/data-access` for each domain library
|
||||||
|
- `@generated/swagger/<api-name>` for generated clients
|
||||||
|
- `@isa/common/data-access` for shared utilities
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
- Services integrate with NgRx signal stores in feature libraries
|
||||||
|
- `EntityContainer<T>` pattern supports lazy loading in state management
|
||||||
|
- Resource factory pattern used for async state management (see remission examples)
|
||||||
|
- Caching implemented via decorators (`@Cache`, `@InFlight`)
|
||||||
|
|
||||||
|
### Runtime & Performance
|
||||||
|
- Zod schema validation adds minimal runtime overhead
|
||||||
|
- Generated clients are tree-shakeable
|
||||||
|
- AbortSignal support enables request cancellation
|
||||||
|
- Caching decorators reduce redundant API calls
|
||||||
|
- `firstValueFrom` pattern avoids memory leaks from subscriptions
|
||||||
|
|
||||||
|
### Security & Compliance
|
||||||
|
- All API calls go through generated clients with consistent auth handling
|
||||||
|
- Input validation via Zod schemas prevents injection attacks
|
||||||
|
- AbortSignal support enables proper request cleanup
|
||||||
|
- Logging excludes sensitive data through structured context
|
||||||
|
|
||||||
|
### Observability & Logging
|
||||||
|
- Consistent logging via `@isa/core/logging` with service-level context
|
||||||
|
- Structured logging with operation context and request metadata
|
||||||
|
- Error logging includes request details without sensitive data
|
||||||
|
- Debug logging for development troubleshooting
|
||||||
|
|
||||||
|
### DX / Tooling
|
||||||
|
- Consistent patterns reduce learning curve across domains
|
||||||
|
- Type inference from Zod schemas eliminates manual type definitions
|
||||||
|
- Auto-completion from TypeScript interfaces
|
||||||
|
- Standard error handling patterns
|
||||||
|
|
||||||
|
## Detailed Design Elements
|
||||||
|
|
||||||
|
### Schema Validation Pattern
|
||||||
|
**Structure:**
|
||||||
|
```typescript
|
||||||
|
// Input validation schema
|
||||||
|
export const SearchByTermSchema = z.object({
|
||||||
|
searchTerm: z.string().min(1, 'Search term must not be empty'),
|
||||||
|
skip: z.number().int().min(0).default(0),
|
||||||
|
take: z.number().int().min(1).max(100).default(20),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Type inference
|
||||||
|
export type SearchByTerm = z.infer<typeof SearchByTermSchema>;
|
||||||
|
export type SearchByTermInput = z.input<typeof SearchByTermSchema>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Model Extension Pattern
|
||||||
|
**Generated DTO Extension:**
|
||||||
|
```typescript
|
||||||
|
import { ProductDTO } from '@generated/swagger/cat-search-api';
|
||||||
|
|
||||||
|
export interface Product extends ProductDTO {
|
||||||
|
name: string;
|
||||||
|
contributors: string;
|
||||||
|
catalogProductNumber: string;
|
||||||
|
// Domain-specific enhancements
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Entity Container Pattern:**
|
||||||
|
```typescript
|
||||||
|
export interface Return extends ReturnDTO {
|
||||||
|
id: number;
|
||||||
|
receipts: EntityContainer<Receipt>[]; // Lazy-loaded relationships
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Implementation Pattern
|
||||||
|
**Standard service structure:**
|
||||||
|
```typescript
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class DomainService {
|
||||||
|
#apiService = inject(GeneratedApiService);
|
||||||
|
#logger = logger(() => ({ service: 'DomainService' }));
|
||||||
|
|
||||||
|
async fetchData(params: InputType, abortSignal?: AbortSignal): Promise<ResultType> {
|
||||||
|
const validated = ValidationSchema.parse(params);
|
||||||
|
|
||||||
|
let req$ = this.#apiService.operation(validated);
|
||||||
|
if (abortSignal) {
|
||||||
|
req$ = req$.pipe(takeUntilAborted(abortSignal));
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await firstValueFrom(req$);
|
||||||
|
|
||||||
|
if (res.error) {
|
||||||
|
this.#logger.error('Operation failed', new Error(res.message));
|
||||||
|
throw new ResponseArgsError(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.result as ResultType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling Strategy
|
||||||
|
1. **Input Validation**: Zod schemas validate and transform inputs
|
||||||
|
2. **API Error Handling**: Check `res.error` from generated clients
|
||||||
|
3. **Structured Logging**: Log errors with context via `@isa/core/logging`
|
||||||
|
4. **Error Propagation**: Throw `ResponseArgsError` for consistent handling
|
||||||
|
|
||||||
|
### Concurrency & Cancellation
|
||||||
|
- **AbortSignal Support**: All async operations accept optional AbortSignal
|
||||||
|
- **RxJS Integration**: Use `takeUntilAborted` operator for cancellation
|
||||||
|
- **Promise Pattern**: `firstValueFrom` prevents subscription memory leaks
|
||||||
|
- **Caching**: `@InFlight` decorator prevents duplicate concurrent requests
|
||||||
|
|
||||||
|
### Extension Points
|
||||||
|
- **Custom Decorators**: `@Cache`, `@InFlight`, `@CacheTimeToLive`
|
||||||
|
- **Schema Transformations**: Zod `.transform()` for data normalization
|
||||||
|
- **Model Inheritance**: Interface extension for domain-specific properties
|
||||||
|
- **Service Composition**: Services can depend on other domain services
|
||||||
|
|
||||||
|
## Code Examples
|
||||||
|
|
||||||
|
### Complete Data-Access Library Structure
|
||||||
|
```typescript
|
||||||
|
// libs/domain/data-access/src/lib/schemas/fetch-items.schema.ts
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const FetchItemsSchema = z.object({
|
||||||
|
categoryId: z.string().min(1),
|
||||||
|
skip: z.number().int().min(0).default(0),
|
||||||
|
take: z.number().int().min(1).max(100).default(20),
|
||||||
|
filters: z.record(z.any()).default({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FetchItems = z.infer<typeof FetchItemsSchema>;
|
||||||
|
export type FetchItemsInput = z.input<typeof FetchItemsSchema>;
|
||||||
|
|
||||||
|
// libs/domain/data-access/src/lib/models/item.ts
|
||||||
|
import { ItemDTO } from '@generated/swagger/domain-api';
|
||||||
|
import { EntityContainer } from '@isa/common/data-access';
|
||||||
|
import { Category } from './category';
|
||||||
|
|
||||||
|
export interface Item extends ItemDTO {
|
||||||
|
id: number;
|
||||||
|
displayName: string;
|
||||||
|
category: EntityContainer<Category>;
|
||||||
|
// Domain-specific enhancements
|
||||||
|
isAvailable: boolean;
|
||||||
|
formattedPrice: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// libs/domain/data-access/src/lib/services/item.service.ts
|
||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { ItemService as GeneratedItemService } from '@generated/swagger/domain-api';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { takeUntilAborted, ResponseArgsError } from '@isa/common/data-access';
|
||||||
|
import { logger } from '@isa/core/logging';
|
||||||
|
import { FetchItemsInput, FetchItemsSchema } from '../schemas';
|
||||||
|
import { Item } from '../models';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ItemService {
|
||||||
|
#itemService = inject(GeneratedItemService);
|
||||||
|
#logger = logger(() => ({ service: 'ItemService' }));
|
||||||
|
|
||||||
|
async fetchItems(
|
||||||
|
params: FetchItemsInput,
|
||||||
|
abortSignal?: AbortSignal
|
||||||
|
): Promise<Item[]> {
|
||||||
|
this.#logger.debug('Fetching items', () => ({ params }));
|
||||||
|
|
||||||
|
const { categoryId, skip, take, filters } = FetchItemsSchema.parse(params);
|
||||||
|
|
||||||
|
let req$ = this.#itemService.getItems({
|
||||||
|
categoryId,
|
||||||
|
queryToken: { skip, take, filter: filters }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (abortSignal) {
|
||||||
|
req$ = req$.pipe(takeUntilAborted(abortSignal));
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await firstValueFrom(req$);
|
||||||
|
|
||||||
|
if (res.error) {
|
||||||
|
this.#logger.error('Failed to fetch items', new Error(res.message));
|
||||||
|
throw new ResponseArgsError(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#logger.info('Successfully fetched items', () => ({
|
||||||
|
count: res.result?.length || 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
return res.result as Item[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// libs/domain/data-access/src/index.ts
|
||||||
|
export * from './lib/models';
|
||||||
|
export * from './lib/schemas';
|
||||||
|
export * from './lib/services';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage in Feature Components
|
||||||
|
```typescript
|
||||||
|
// feature component using the data-access library
|
||||||
|
import { Component, inject, signal } from '@angular/core';
|
||||||
|
import { ItemService, Item, FetchItemsInput } from '@isa/domain/data-access';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-item-list',
|
||||||
|
template: `
|
||||||
|
@if (loading()) {
|
||||||
|
<div>Loading...</div>
|
||||||
|
} @else {
|
||||||
|
@for (item of items(); track item.id) {
|
||||||
|
<div>{{ item.displayName }}</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class ItemListComponent {
|
||||||
|
#itemService = inject(ItemService);
|
||||||
|
|
||||||
|
items = signal<Item[]>([]);
|
||||||
|
loading = signal(false);
|
||||||
|
|
||||||
|
async loadItems(categoryId: string) {
|
||||||
|
this.loading.set(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params: FetchItemsInput = {
|
||||||
|
categoryId,
|
||||||
|
take: 50,
|
||||||
|
filters: { active: true }
|
||||||
|
};
|
||||||
|
|
||||||
|
const items = await this.#itemService.fetchItems(params);
|
||||||
|
this.items.set(items);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load items', error);
|
||||||
|
} finally {
|
||||||
|
this.loading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Migration Pattern for Existing Services
|
||||||
|
```typescript
|
||||||
|
// Before: Direct HTTP client usage
|
||||||
|
@Injectable()
|
||||||
|
export class LegacyItemService {
|
||||||
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
getItems(categoryId: string): Observable<any> {
|
||||||
|
return this.http.get(`/api/items?category=${categoryId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After: Standardized data-access pattern
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ItemService {
|
||||||
|
#itemService = inject(GeneratedItemService);
|
||||||
|
#logger = logger(() => ({ service: 'ItemService' }));
|
||||||
|
|
||||||
|
async fetchItems(params: FetchItemsInput, abortSignal?: AbortSignal): Promise<Item[]> {
|
||||||
|
const validated = FetchItemsSchema.parse(params);
|
||||||
|
// ... standard implementation pattern
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Open Questions / Follow-Ups
|
||||||
|
- Unresolved design clarifications
|
||||||
|
- Dependent ADRs required
|
||||||
|
- External approvals needed
|
||||||
|
|
||||||
|
## Decision Review & Revalidation
|
||||||
|
When and how this ADR will be re-evaluated (date, trigger conditions, metrics thresholds).
|
||||||
|
|
||||||
|
## Status Log
|
||||||
|
| Date | Change | Author |
|
||||||
|
|------|--------|--------|
|
||||||
|
| 2025-09-29 | Created (Draft) | Lorenz, Nino |
|
||||||
|
| 2025-09-25 | Analysis completed, comprehensive patterns documented | AI Assistant |
|
||||||
|
|
||||||
|
## References
|
||||||
|
**Existing Implementation Examples:**
|
||||||
|
- `libs/catalogue/data-access` - Basic schema and service patterns
|
||||||
|
- `libs/remission/data-access` - Advanced patterns with EntityContainer and stores
|
||||||
|
- `libs/common/data-access` - Shared utilities and response types
|
||||||
|
- `generated/swagger/` - Generated API clients integration
|
||||||
|
|
||||||
|
**Key Dependencies:**
|
||||||
|
- [Zod](https://github.com/colinhacks/zod) - Schema validation library
|
||||||
|
- [ng-swagger-gen](https://github.com/cyclosproject/ng-swagger-gen) - OpenAPI client generation
|
||||||
|
- `@isa/core/logging` - Structured logging infrastructure
|
||||||
|
- `@isa/common/data-access` - Shared utilities and types
|
||||||
|
|
||||||
|
**Related Documentation:**
|
||||||
|
- ISA Frontend Copilot Instructions - Data-access patterns
|
||||||
|
- Tech Stack Documentation - Architecture overview
|
||||||
|
- Code Style Guidelines - TypeScript and Angular patterns
|
||||||
|
|
||||||
|
---
|
||||||
|
> Document updates MUST reference this ADR number in commit messages: `ADR-NNNN:` prefix.
|
||||||
|
> Keep this document updated through all lifecycle stages.
|
||||||
Reference in New Issue
Block a user