ReadKeep/docs/Architecture.md
Ilyas Hallak 6de413376f Clean up documentation and remove debug settings
- Move documentation files from root to docs/ folder
- Remove DEBUG-only settings from ReadingSettingsView (Safari Reader Mode, Auto-mark as read)
2025-11-21 22:07:41 +01:00

133 lines
5.3 KiB
Markdown

// SPDX-License-Identifier: MIT
# Architecture Overview: readeck client
## 1. Introduction
**readeck client** is an open-source iOS project for conveniently managing and reading bookmarks. The app uses the MVVM architecture pattern and follows a clear layer structure: **UI**, **Domain**, and **Data**. A key feature is its own dependency injection (DI) based on Swift protocols and the factory pattern—completely without external libraries.
- **Architecture Pattern:** MVVM (Model-View-ViewModel) + Use Cases
- **Layers:** UI, Domain, Data
- **Technologies:** Swift, SwiftUI, CoreData, custom DI
- **DI:** Protocol-based, factory pattern, no external libraries
## 2. Architecture Overview
```mermaid
graph TD
UI["UI Layer (View, ViewModel)"]
Domain["Domain Layer (Use Cases, Models, Repository Protocols)"]
Data["Data Layer (Repository implementations, Database, Entities, API)"]
UI --> Domain
Domain --> Data
```
**Layer Overview:**
| Layer | Responsibility |
|---------|----------------------|
| UI | Presentation, user interaction, ViewModels, bindings |
| Domain | Business logic, use cases, models, repository protocols |
| Data | Repository implementations, database, entities, API |
## 3. Dependency Injection (DI)
**Goal:** Loose coupling, better testability, exchangeability of implementations.
**Approach:**
- Define protocols for dependencies (e.g., repository protocols)
- Implement the protocols in concrete classes
- Provide dependencies via a central factory
- Pass dependencies to ViewModels/use cases via initializers
**Example:**
```swift
// 1. Protocol definition
protocol PBookmarksRepository {
func getBookmarks() async throws -> [Bookmark]
}
// 2. Implementation
class BookmarksRepository: PBookmarksRepository {
func getBookmarks() async throws -> [Bookmark] {
// ...
}
}
// 3. Factory
class DefaultUseCaseFactory {
let bookmarksRepository: PBookmarksRepository = BookmarksRepository()
func makeGetBookmarksUseCase() -> GetBookmarksUseCase {
GetBookmarksUseCase(bookmarksRepository: bookmarksRepository)
}
}
// 4. ViewModel
class BookmarksViewModel: ObservableObject {
private let getBookmarksUseCase: GetBookmarksUseCase
init(factory: DefaultUseCaseFactory) {
self.getBookmarksUseCase = factory.makeGetBookmarksUseCase()
}
}
```
**Advantages:**
- Exchangeability (e.g., for tests)
- No dependency on frameworks
- Central management of all dependencies
## 4. Component Description
| Component | Responsibility |
|---------------------|---------------|
| View | UI elements, presentation, user interaction |
| ViewModel | Bridge between View & Domain, state management |
| Use Case | Encapsulates a business logic (e.g., create bookmark) |
| Repository Protocol | Interface between Domain & Data layer |
| Repository Implementation | Concrete implementation of repository protocols, handles data access |
| Data Source / API | Access to external data sources (API, CoreData, Keychain) |
| Model/Entity | Represents core data structures |
| Dependency Factory | Creates and manages dependencies, central DI point |
## 5. Data Flow
1. **User interaction** in the view triggers an action in the ViewModel.
2. The **ViewModel** calls a **use case**.
3. The **use case** uses a **repository protocol** to load/save data.
4. The **repository implementation** accesses a **data source** (e.g., API, CoreData).
5. The response flows back up to the view and is displayed.
## 6. Advantages of this Architecture
- **Testability:** Protocols and DI allow components to be tested in isolation.
- **Maintainability:** Clear separation of concerns, easy extensibility.
- **Modularity:** Layers can be developed and adjusted independently.
- **Independence:** No dependency on external DI or architecture frameworks.
## 7. Contributor Tips
- **New dependencies:** Always define as a protocol and register in the factory.
- **Protocols:** Define in the domain layer, implement in the data layer.
- **Factory:** Extend the factory for new use cases or repositories.
- **No external frameworks:** Intentionally use custom solutions for better control and clarity.
## 8. Glossary
| Term | Definition |
|---------------------|------------|
| Dependency Injection| Technique for providing dependencies from the outside |
| Protocol | Swift interface that defines requirements for types |
| Factory Pattern | Design pattern for central object creation |
| MVVM | Architecture: Model-View-ViewModel |
| Use Case | Encapsulates a specific business logic |
| Repository Protocol | Interface in the domain layer for data access |
| Repository Implementation | Concrete class in the data layer that fulfills a repository protocol |
| Data Source | Implementation for data access (API, DB, etc.) |
| Model/Entity | Core data structure used in domain or data layer |
## 9. Recommended Links
- [Clean Architecture (Uncle Bob)](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
- [Clean Architecture for Swift/iOS (adrian bilescu)](https://adrian-bilescu.medium.com/a-pragmatic-guide-to-clean-architecture-on-ios-e58d19d00559)
- [Swift.org: Protocols](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/protocols/)