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