Skip to content

Commit 926b00c

Browse files
docs(Common): Update architecture documentation to reflect final naming and structure
Updated `Deep Dive.md` and `README.md` to align with the finalized architecture and naming conventions established in recent refactors. Key changes include: - Renamed core traits (`FsReader` → `FileSystemReader`, `AppRuntime` → `ApplicationRunTime`) to improve clarity - Updated module references (`effect/` → `Effect/`, `dto/` → `DTO/`) to match actual source structure - Revised `ActionEffect` type parameters (`RuntimeAccess`→`TCapability`) to emphasize capability-focused design - Simplified effect constructor examples by removing generic runtime constraints - Corrected directory paths throughout to reflect current project layout - Enhanced explanations of dependency injection and service provider patterns These updates ensure documentation accurately reflects the abstract core architecture defined in `Common`, which serves as the foundation for `Mountain`'s implementation and `Cocoon`'s gRPC contracts. The changes support all implemented workflows by providing clear guidance for extending capabilities through the established `ActionEffect` and DI systems.
1 parent 5adcc5e commit 926b00c

File tree

2 files changed

+94
-89
lines changed

2 files changed

+94
-89
lines changed

README.md

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ implementing the traits and consuming the effects defined in this crate.
3131
definition of an operation and its execution.
3232
2. **Provide a Declarative Effect System:** Introduces the `ActionEffect` type,
3333
which describes an asynchronous operation as a value, allowing logic to be
34-
composed, tested, and executed in a controlled `AppRuntime`.
34+
composed, tested, and executed in a controlled `ApplicationRunTime`.
3535
3. **Standardize Data Contracts:** Defines all Data Transfer Objects (DTOs) and
3636
a universal `CommonError` enum, ensuring consistent data structures and
37-
error handling across the entire native ecosystem, including the gRPC API.
37+
error handling across the entire native ecosystem.
3838
4. **Maximize Testability and Reusability:** Because this crate is pure and
3939
abstract, any component that depends on it can be tested with mock
4040
implementations of its traits, leading to fast and reliable unit tests.
@@ -50,8 +50,8 @@ implementing the traits and consuming the effects defined in this crate.
5050
the `Environment` and `Requires` traits, allowing components to declare their
5151
dependencies without being tied to a specific implementation.
5252
- **Asynchronous Service Traits:** All core application services (e.g.,
53-
`FsReader`, `UiProvider`, `CommandExecutor`) are defined as `async trait`s,
54-
providing a fully asynchronous-first architecture.
53+
`FileSystemReader`, `UserInterfaceProvider`, `CommandExecutor`) are defined as
54+
`async trait`s, providing a fully asynchronous-first architecture.
5555
- **Comprehensive DTO Library:** Contains definitions for all data structures
5656
used for IPC communication with `Cocoon` and internal state management in
5757
`Mountain`. All types are `serde`-compatible.
@@ -66,9 +66,9 @@ implementing the traits and consuming the effects defined in this crate.
6666
| Principle | Description | Key Components Involved |
6767
| :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- |
6868
| **Abstraction** | Define every application capability as an abstract `async trait`. Never include concrete implementation logic. | All `*Provider.rs` and `*Manager.rs` files |
69-
| **Declarativism** | Represent every operation as an `ActionEffect` value. The crate provides constructor functions for these effects. | `effect/*`, all `*Effect.rs` files |
70-
| **Composability** | The `ActionEffect` system and trait-based DI are designed to be composed, allowing complex workflows to be built from simple, reusable pieces. | `environment/*`, `effect/*` |
71-
| **Contract-First** | Define all data structures (`dto/*`) and error types (`error/*`) first. These form the stable contract for all other components. | `dto/`, `error/` |
69+
| **Declarativism** | Represent every operation as an `ActionEffect` value. The crate provides constructor functions for these effects. | `Effect/*`, all effect constructor files |
70+
| **Composability** | The `ActionEffect` system and trait-based DI are designed to be composed, allowing complex workflows to be built from simple, reusable pieces. | `Environment/*`, `Effect/*` |
71+
| **Contract-First** | Define all data structures (`DTO/*`) and error types (`Error/*`) first. These form the stable contract for all other components. | `DTO/`, `Error/` |
7272
| **Purity** | This crate has minimal dependencies and is completely independent of Tauri, gRPC, or any specific application logic. | `Cargo.toml` |
7373

7474
---
@@ -82,21 +82,24 @@ _returns a description of that effect_.
8282
**Traditional (Imperative) Approach:**
8383

8484
```rust
85-
async fn read_my_file(fs: &impl Fs) -> Result<Vec<u8>, Error> {
85+
async fn read_my_file(fs: &impl FileSystem) -> Result<Vec<u8>, Error> {
8686
fs.read("/path/to/file").await // The side effect happens here.
8787
}
8888
```
8989

9090
**The `Common` (Declarative) Approach:**
9191

9292
```rust
93-
use Common::fs;
93+
use Common::FileSystem;
94+
use std::sync::Arc;
9495

9596
// 1. Create a description of the desired effect. No I/O happens here.
96-
let read_effect = fs::ReadFile(PathBuf::from("/path/to/file"));
97+
// The effect's type signature explicitly declares its dependency: `Arc<dyn FileSystemReader>`.
98+
let read_effect: ActionEffect<Arc<dyn FileSystemReader>, _, _> = FileSystem::ReadFile(PathBuf::from("/path/to/file"));
9799

98100
// 2. Later, in a separate part of the system (the runtime), execute it.
99-
// The runtime provides the concrete implementation of the `FsReader` trait.
101+
// The runtime will see that the effect needs a FileSystemReader, provide one from its
102+
// environment, and run the operation.
100103
let file_content = runtime.Run(read_effect).await?;
101104
```
102105

@@ -112,15 +115,17 @@ its trait definitions, DTOs, and effect constructors.
112115
```
113116
Common/
114117
└── Source/
115-
├── lib.rs # Crate root, declares all modules.
116-
├── environment/ # The core DI system (Environment, Requires traits).
117-
├── effect/ # The ActionEffect system (ActionEffect, AppRuntime traits).
118-
├── error/ # The universal CommonError enum.
119-
└── command/ # Example service domain:
120-
├── mod.rs # Module aggregator.
121-
├── CommandExecutor.rs # The abstract `trait` definition.
122-
└── ExecuteCommand.rs # The `ActionEffect` constructor function.
123-
└── (Other service domains like fs/, ui/, terminal/, etc. follow the same pattern)
118+
├── Library.rs # Crate root, declares all modules.
119+
├── Environment/ # The core DI system (Environment, Requires traits).
120+
├── Effect/ # The ActionEffect system (ActionEffect, ApplicationRunTime traits).
121+
├── Error/ # The universal CommonError enum.
122+
├── DTO/ # Shared Data Transfer Objects.
123+
└── Command/ # Example service domain:
124+
├── mod.rs # Module aggregator.
125+
├── CommandExecutor.rs # The abstract `trait` definition.
126+
├── DTO/ # (if any service-specific DTOs are needed)
127+
└── ExecuteCommand.rs # The `ActionEffect` constructor function.
128+
└── (Other service domains like FileSystem/, UserInterface/, etc. follow the same pattern)
124129
```
125130

126131
---
@@ -149,9 +154,9 @@ graph LR
149154
150155
subgraph "The `Common` Crate"
151156
direction LR
152-
Traits["Abstract Traits (e.g., `FsReader`)"]:::common
157+
Traits["Abstract Traits (e.g., `FileSystemReader`)"]:::common
153158
Effects["ActionEffects (e.g., `ReadFile`)"]:::common
154-
DTOs["Data Transfer Objects (e.g., `FileTypeDto`)"]:::common
159+
DTOs["Data Transfer Objects (e.g., `FileTypeDTO`)"]:::common
155160
156161
Effects -- Depend on --> Traits
157162
end
@@ -188,34 +193,33 @@ Common = { path = "../Common" }
188193
A developer working within the `Mountain` codebase would use `Common` as
189194
follows:
190195

191-
1. **Implement a Trait:** In `Mountain/Source/environment/`, provide the
196+
1. **Implement a Trait:** In `Mountain/Source/Environment/`, provide the
192197
concrete implementation for a `Common` trait.
193198

194199
```rust
195-
// In Mountain/Source/environment/FsProvider.rs
196-
use Common::fs::{FsReader, FsWriter};
200+
// In Mountain/Source/Environment/FileSystemProvider.rs
201+
use Common::FileSystem::{FileSystemReader, FileSystemWriter};
197202

198203
#[async_trait]
199-
impl FsReader for MountainEnvironment {
204+
impl FileSystemReader for MountainEnvironment {
200205
async fn ReadFile(&self, Path: &PathBuf) -> Result<Vec<u8>, CommonError> {
201-
// Delegate to the handler which contains the actual `tokio::fs` call.
202-
handlers::fs::ReadFileLogic(&self.AppHandle, Path).await
206+
// ... actual `tokio::fs` call ...
203207
}
204208
// ...
205209
}
206210
```
207211

208-
2. **Create and Execute an Effect:** In a command handler or other logic file,
209-
create and run an effect.
212+
2. **Create and Execute an Effect:** In business logic, create and run an
213+
effect.
210214

211215
```rust
212-
// In a Mountain handler
213-
use Common::fs;
214-
use Common::effect::AppRuntime;
216+
// In a Mountain service or command
217+
use Common::FileSystem;
218+
use Common::Effect::ApplicationRunTime;
215219

216-
async fn some_logic(runtime: Arc<impl AppRuntime>) {
220+
async fn some_logic(runtime: Arc<impl ApplicationRunTime>) {
217221
let path = PathBuf::from("/my/file.txt");
218-
let read_effect = fs::ReadFile(path);
222+
let read_effect = FileSystem::ReadFile(path);
219223

220224
match runtime.Run(read_effect).await {
221225
Ok(content) => info!("File content length: {}", content.len()),

docs/Deep Dive.md

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ The architecture of the `Common` crate is built on two fundamental principles:
2222

2323
1. **Separation of Concerns (The "What" vs. The "How"):** `Common` is strictly
2424
concerned with defining **what** the application can do. It does this by
25-
defining abstract `trait`s (e.g., `trait FsReader`). It has absolutely no
26-
knowledge of **how** these actions are performed. The concrete
25+
defining abstract `trait`s (e.g., `trait FileSystemReader`). It has
26+
absolutely no knowledge of **how** these actions are performed. The concrete
2727
implementation (the "how") is the responsibility of another crate,
2828
`Mountain`, which might use `tokio::fs` or a network client to fulfill the
2929
contract. This is a form of the "Ports and Adapters" architecture.
@@ -39,23 +39,23 @@ The architecture of the `Common` crate is built on two fundamental principles:
3939

4040
## Detailed Component Breakdown
4141

42-
### 1. The `ActionEffect` System (`effect/`)
42+
### 1. The `ActionEffect` System (`Effect/`)
4343

4444
This is the most important concept in the `Common` crate.
4545

46-
- **`ActionEffect<RuntimeAccess, Error, Output>` Struct:**
46+
- **`ActionEffect<TCapability, TError, TOutput>` Struct:**
4747

4848
- This struct does not contain logic. It simply wraps an
4949
`Arc<dyn Fn(...) -> Future>`.
5050
- The function it wraps is the "potential" operation. The `Arc` makes the
5151
effect cheap to clone and pass around.
5252
- The type parameters define the contract:
53-
- `RuntimeAccess`: The type of capability the effect needs to run (e.g.,
54-
`Arc<dyn FsReader>`).
55-
- `Error`: The type of error it can fail with (always `CommonError`).
56-
- `Output`: The type of value it will produce on success.
53+
- `TCapability`: The type of capability the effect's closure needs to
54+
run (e.g., `Arc<dyn FileSystemReader>`).
55+
- `TError`: The type of error it can fail with (always `CommonError`).
56+
- `TOutput`: The type of value it will produce on success.
5757

58-
- **`AppRuntime` Trait:**
58+
- **`ApplicationRunTime` Trait:**
5959
- This trait defines the contract for an "executor". Its primary method is
6060
`Run`.
6161
- The `Run` method is what takes an `ActionEffect` value, provides it with
@@ -67,53 +67,53 @@ This is the most important concept in the `Common` crate.
6767
```rust
6868
// 1. A constructor function in `Common` creates an effect.
6969
// No I/O happens here. It just creates a struct.
70-
let read_effect = Common::fs::ReadFile(path);
70+
let read_effect = Common::FileSystem::ReadFile(path);
7171

7272
// 2. The effect is passed to a runtime.
7373
let result = my_runtime.Run(read_effect).await;
7474

7575
// 3. Inside the runtime's Run method:
76-
// a. It gets its environment.
77-
// b. It asks the environment for the required capability: `env.Require::<Arc<dyn FsReader>>()`.
78-
// c. It calls the effect's internal function with the capability.
76+
// a. It gets its environment using self.GetEnvironment().
77+
// b. It asks the environment for the required capability: `env.Require::<Arc<dyn FileSystemReader>>()`.
78+
// c. It calls the effect's internal function, passing in the required capability.
7979
// d. The function is awaited, and the actual tokio::fs::read() call is finally made.
8080
```
8181

82-
### 2. The Dependency Injection System (`environment/`)
82+
### 2. The Dependency Injection System (`Environment/`)
8383

8484
- **`Environment` Trait:** A simple marker trait. Any struct that can act as a
8585
dependency container for our application must implement this trait.
8686
- **`Requires<Capability>` Trait:** This is the core of the DI system. A struct
8787
implementing `Requires<T>` guarantees that it can provide an instance of `T`.
8888
- **Usage:** Our `MountainEnvironment` will implement
89-
`Requires<Arc<dyn FsReader>>`, `Requires<Arc<dyn UiProvider>>`, etc., for
90-
every service trait. The `AppRuntime` uses these implementations to provide
91-
the necessary dependencies to the effects it runs. This decouples the effects
92-
from the concrete environment.
89+
`Requires<Arc<dyn FileSystemReader>>`,
90+
`Requires<Arc<dyn UserInterfaceProvider>>`, etc., for every service trait. The
91+
`ApplicationRunTime` uses these implementations to provide the necessary
92+
dependencies to the effects it runs. This decouples the effects from the
93+
concrete environment.
9394

9495
### 3. The Service Provider Pattern
9596

96-
Every functional domain in `Common` (e.g., `fs`, `ui`, `command`) follows a
97-
strict pattern:
97+
Every functional domain in `Common` (e.g., `FileSystem`, `UserInterface`,
98+
`Command`) follows a strict pattern:
9899

99100
1. **A Trait Definition (`MyService/MyServiceProvider.rs`):** An `async trait`
100101
that defines the high-level capabilities of the service (e.g.,
101-
`trait FsReader { async fn ReadFile(...); }`).
102-
2. **DTOs (`MyService/dto/`):** A submodule containing all `serde`-compatible
102+
`trait FileSystemReader { async fn ReadFile(...); }`).
103+
2. **DTOs (`MyService/DTO/`):** A submodule containing all `serde`-compatible
103104
`struct`s and `enum`s that are used as parameters or return types for the
104-
service's methods. These DTOs form the stable data contract for gRPC and
105-
internal use.
105+
service's methods. These DTOs form the stable data contract.
106106
3. **Effect Constructors (`MyService/MyEffect.rs`):** A set of public
107107
functions, one for each method in the service trait. Each function takes the
108108
same arguments as the trait method but returns an `ActionEffect` instead of
109109
executing the logic directly.
110110

111-
### 4. The Universal Error (`error/CommonError.rs`)
111+
### 4. The Universal Error (`Error/CommonError.rs`)
112112

113113
- To maintain predictability, every `ActionEffect` returns a
114114
`Result<T, CommonError>`.
115115
- `CommonError` is a single, comprehensive `enum` that covers all possible
116-
failure domains (Filesystem, IPC, UI, etc.).
116+
failure domains (FileSystem, IPC, UI, etc.).
117117
- This pattern allows consumers of effects to handle all possible errors with a
118118
single `match` statement, while still providing specific, tagged error
119119
variants for precise handling when needed. It uses `thiserror::Error` for
@@ -125,55 +125,56 @@ strict pattern:
125125

126126
Adding a new capability to the application follows a clear, repeatable recipe:
127127

128-
1. **Create the Module:** Create a new directory in `src/`, for example,
129-
`src/new_service/`. Create a `mod.rs` inside it.
128+
1. **Create the Module:** Create a new directory, e.g., `Source/NewService/`,
129+
and a `mod.rs` inside it.
130130

131-
2. **Define the Trait:** In `src/new_service/NewServiceProvider.rs`, define the
132-
new `async trait`:
131+
2. **Define the Trait:** In `Source/NewService/NewServiceProvider.rs`, define
132+
the new `async trait`:
133133

134134
```rust
135135
#[async_trait]
136136
pub trait NewServiceProvider: Environment {
137-
async fn DoSomething(&self, Options: MyOptionsDto) -> Result<MyResultDto, CommonError>;
137+
async fn DoSomething(&self, Options: MyOptionsDTO) -> Result<MyResultDTO, CommonError>;
138138
}
139139
```
140140

141-
3. **Define the DTOs:** In `src/new_service/dto/`, create files for
141+
3. **Define the DTOs:** In `Source/NewService/DTO/`, create files for
142142
`MyOptionsDto.rs` and `MyResultDto.rs` with
143143
`#[derive(Serialize, Deserialize)]`.
144144

145-
4. **Update `CommonError`:** In `src/error/CommonError.rs`, add a new variant
146-
for failures related to your service:
145+
4. **Update `CommonError`:** In `Source/Error/CommonError.rs`, add a new
146+
variant for failures related to your service:
147147

148148
```rust
149149
#[error("NewService failed: {Reason}")]
150150
NewServiceError { Reason: String },
151151
```
152152

153-
5. **Create the Effect Constructor:** In `src/new_service/DoSomething.rs`,
154-
create the function that returns the `ActionEffect`:
153+
5. **Create the Effect Constructor:** In `Source/NewService/DoSomething.rs`,
154+
create the function that returns the `ActionEffect`. Note its signature:
155+
it's no longer generic, and its dependency is explicit.
155156

156157
```rust
157-
pub fn DoSomething<Runtime>(
158-
Options: MyOptionsDto,
159-
) -> ActionEffect<Arc<Runtime>, CommonError, MyResultDto>
160-
where
161-
Runtime: AppRuntime + Send + Sync + 'static,
162-
Runtime::EnvironmentType: Requires<Arc<dyn NewServiceProvider>>,
163-
{
164-
ActionEffect::New(Arc::new(move |Runtime: Arc<Runtime>| {
158+
use std::sync::Arc;
159+
use crate::Effect::ActionEffect;
160+
use crate::Error::CommonError;
161+
use super::NewServiceProvider;
162+
163+
pub fn DoSomething(
164+
Options: MyOptionsDTO,
165+
) -> ActionEffect<Arc<dyn NewServiceProvider>, CommonError, MyResultDTO> {
166+
ActionEffect::New(Arc::new(move |Provider: Arc<dyn NewServiceProvider>| {
165167
let OptionsClone = Options.clone();
166168
Box::pin(async move {
167-
let Environment = Runtime.GetEnvironment();
168-
let Provider: Arc<dyn NewServiceProvider> = Environment.Require();
169169
Provider.DoSomething(OptionsClone).await
170170
})
171171
}))
172172
}
173173
```
174174

175-
6. **Export from Modules:** Update `src/new_service/mod.rs` and `src/lib.rs` to
176-
publicly export the new trait, DTOs, and effect constructor.
175+
6. **Export from Modules:** Update `Source/NewService/mod.rs` and
176+
`Source/Library.rs` to publicly export the new trait, DTOs, and effect
177+
constructor.
177178

178179
---
179180

@@ -182,12 +183,12 @@ Adding a new capability to the application follows a clear, repeatable recipe:
182183
- **`Mountain`:** `Mountain` is the primary **implementor** of the traits in
183184
`Common`. The `MountainEnvironment` struct will have
184185
`impl NewServiceProvider for MountainEnvironment` blocks that contain the
185-
concrete logic, which in turn delegate to `handlers`. `Mountain` is also the
186-
home of the `AppRuntime` that **executes** the effects.
187-
- **`Cocoon`:** `Cocoon` is a remote **consumer**. The gRPC methods defined in
188-
`vine.proto` are designed to directly correspond to the effect constructors
189-
and DTOs in `Common`. When `Cocoon` sends a request, `Mountain`'s `Track`
190-
dispatcher creates the corresponding `ActionEffect` from `Common` and runs it.
186+
concrete logic. `Mountain` is also the home of the `ApplicationRunTime` that
187+
**executes** the effects.
188+
- **`Cocoon`:** `Cocoon` is a remote **consumer**. When `Cocoon` sends a
189+
request, `Mountain`'s `Track` dispatcher creates the corresponding
190+
`ActionEffect` from `Common` and runs it. The DTOs defined in `Common` serve
191+
as the data contract for this communication.
191192

192193
This clear separation ensures that `Common` remains the universal, abstract
193194
blueprint for all native backend functionality.

0 commit comments

Comments
 (0)