This commit is contained in:
appflowy 2021-06-21 22:53:31 +08:00
parent 2053cd7f05
commit 126ac20ccc
11 changed files with 292 additions and 263 deletions

3
.gitignore vendored
View File

@ -9,5 +9,4 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
/rust-lib/flowy-ast
/rust-lib/flowy-derive
/rust-lib

View File

@ -1,38 +1,22 @@
# AppFlowy client
* The Big Picture
* Introduce
* Goals
* Cross-platform
* Getting started
* Build
* Running
* Contributing
* First steps
* Opening issues
* Participating in discussions
* Finding something to work on
* Open your PR
* Review Process
* Getting more involved
* Organization membership
* Contributor
* Maintainer
## The Big Picture
### Introduce
### Goals
### Cross-platform
[![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
![Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg)
## Getting Started
Read the [architecture doc](doc/architecture.md) before you started.
# what is AppFlowy?
## Features
## Documentation
* [**Getting Started**](doc/getting_started.md)
* [**Roadmap**](doc/roadmap.md)
* [**Deep Dive AppFlowy**](doc/architecture.md)
## Contributing
Read the [architecture doc](doc/architecture.md) before you started.
Read the [Contributing Doc](doc/contribute.md) before you want to contribute.
## Social Media
* Slack
## License
AppFlowy is under the Apache 2.0 license. See the [LICENSE](/LICENSE) file for details.

1
doc/about app flowy.md Normal file
View File

@ -0,0 +1 @@
[WIP]

View File

@ -1,231 +1,19 @@
# AppFlowy Architecture
This documentation introduces the Domain-Driven Design and the design of AppFlowy. Feel free to skip the first part
if you know what Domain-Driven Design is.
* Basic Concepts
* Layered architecture
* Domain Driven Design
# 🥳 AppFlowy System Design
* AppFlowy Design
* Overview
* Operation Flow
* Create
* Read
* Update
* Delete
* Goals of the System
* Some Design Considerations
* High Level Design
* Component Design
# Basic Concepts
## 🎯 Goals of the System
## Layered architecture
The most common architecture pattern is the layered architecture pattern, known as the n-tier architecture pattern.
Partition the software into `layers` to reduce the complexity. Each layer of the layered architecture pattern has a
specific role and responsibility.
## 🤔 Some Design Considerations
## 📜 High Level Design
## 📚 Component Design
## Domain Driven Design
For many architects, the process of data modeling is driven by intuition. However, there are well-formulated methodologies
for approaching it more formally. I recommend the [Domain-Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design)
and choose it as AppFlowy architecture.
DDD consists of four layers.
```
┌──────────────────────────────────────────────────┐ ─────────▶
│ Presentation Layer │──┐ Dependency
└──────────────────────────────────────────────────┘ │
│ │
▼ │
┌──────────────────────────────────────────────────┐ │
│ Application Layer │ │
└──────────────────────────────────────────────────┘ │
│ │
▼ │
┌──────────────────────────────────────────────────┐ │
│ Domain Layer │◀─┘
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Infrastructure Layer │
└──────────────────────────────────────────────────┘
Level of ├───────────────────┐
Abstraction │ Presentation │
├───────────────────┴───────┐
│ Application │
├───────────────────────────┴─────────┐
│ Domain │
├─────────────────────────────────────┴────────┐
│ Infrastructure │
└──────────────────────────────────────────────┴─────▶
Complexity
```
**Presentation**:
* Responsible for presenting information to the user and interpreting user commands.
* Consists of Widgets and also the state of the Widgets.
**Application**:
* Defines the jobs the software is supposed to do. (Shouldn't find any UI code or network code)
* Coordinates the application activity and delegates work to the next layer down.
* It doesn't contain any complex business logic but the basic validation on the user input before
passing to the other layer.
**Domain**:
* Responsible for representing concepts of the business.
* Manages the business state or delegated to the infrastructure layer.
* Self contained and it doesn't depend on any other layers. Domain should be well isolated from the
other layers.
**Infrastructure**:
This layer acts as a supporting library for all the other layers. It deals with APIs,
databases and network, etc.
DDD classifies data as referenceable objects, or entities, and non-referenceable objects, or values.
**Entity**
user, order, book, table. They are referenceable because they carry an identity which
allows us to reference them.
**Value**
email, phone number, name, age, description. They can't be referenced. They can be only included into
entities and serve as attributes. Values could be simple or could be composite.
**Aggregate**
entities can be grouped into aggregates. Aggregates can simplify the model by accessing th entire
aggregate. For instance, Table has lots of row. Each row using the table_id to reference to the
table. TableAggregate includes two entities: Table and the Row.
```
TableAggregate
┌────────────────────────────────────────────────────────────────┐
│ │
│ ┌────────────────────┐ ┌─────────────────────────┐ │
│ │struct Table { │ │struct Row { │ │
│ │ id: String, │ │ table_id: String, │ │
│ │ desc: String, │◀▶───────│} │ │
│ │} │ │ │ │
│ └────────────────────┘ └─────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
```
DDD also introduces `Service` and `Repository`.
**Service**
When a significant process of transformation in the domain is not a natural responsibility of an `Entity` or `Value object`, add
an operation to the model as standalone interface declared as a Service. For instance: The `Value object`, EmailAddress,
uses the function `validateEmailAddress` to verify the email address is valid or not. `Service` exists in Application, Domain and
Infrastructure.
```
class EmailAddress {
final Either<Failure<String>, String> value;
factory EmailAddress(String? input) {
return EmailAddress._(
validateEmailAddress(input),
);
}
const EmailAddress._(this.value);
}
### 📕 Component 1
### 📗 Component 2
### 📘 Component 3
### 📙 Component 3
Either<Failure<String>, String> validateEmailAddress(String? input) {
...
}
```
**Repository**
Repository offer an interface to retrieve and persist aggregates. They hide the database or network details from the domain.
The Repository interfaces are declared in the Domain Layer, but the repositories themselves are implemented in the Infrastructure Layer.
You can replace the interface implementation without impacting the domain layer. For instance:
```
// Interface:
abstract class AuthInterface {
...
}
// Implementation
class AuthRepository implements AuthInterface {
...
}
```
More often than not, the repository interface can be divided into sub-repository in order to reduce the complexity.
## AppFlowy Design
The AppFlowy Client consists of lots of modules. Each of them follows the DDD design pattern and using [dependency injection](https://levelup.gitconnected.com/dependency-injection-in-swift-bc16d66b038b)
to communication with other module.
```
Client
┌──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ Module A Module B Module C │
│ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │
│ │ presentation │ Dependency │ presentation │ │ presentation │ │
│ │ │ Injection │ │ │ │ │
│ │ application ├───────────────▶│ application │ │ application │ │
│ │ │◀───────────────│ │ │ │ ◉ ◉ ◉ │
│ │ domain │ │ domain │ │ domain │ │
│ │ │ │ │ │ │ │
│ │ Infrastructure │ │ Infrastructure │ │ Infrastructure │ │
│ └───────────────────┘ └───────────────────┘ └───────────────────┘ │
│ ▲ │ │
│ │ Dependency Injection │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────┘
```
Let's dig it how can I construct each module. I take `User` module for demonstration.
```
presentation Application domain Infrastructure
│ │
7 Data Model
┌──────────────────────────────┐ │ ┌────────────────────────┐ │ ┌───────────────────────────┐
│ │ │ │ ┌─────────────┐ │ ┌─────▶│ Repository implementation │
▼ Bloc │ 2.1 │ │ Aggregate │ │ │ │ └───────────────────────────┘
┌─────────────┐ │ ┌─────────────────┴─────┐ │ └─────────────┘ │ │ │ 4
│ Widget │ │ ┌────────┐ ┌────────┐ │ │ │ ┌────────┐ │ │ │ ▼
└─────────────┘ │ │ │ Event │ │ State │ │───┬────▶│ │ Entity │ │ │ ┌─────────────────────┐
│ │ └────────┘ └────────┘ │ │ │ │ └────────┘ │ │ │ │ Unit of Work │
│ │ └──────▲────────────────┘ │ │ ┌─────────────────┐ │ │ └─────────────────────┘
│ │ │ │ │ │ Value Object │ │ │ │ │ 5
└──────────┼────────┘ │ │ └─────────────────┘ │ │ ▼
1 │ │ └────────────────────────┘ │ │ ┌─────────────────────┐
│ │ ◈ │ │ Persistence Service │
│ │ │ contain │ │ └─────────────────────┘
│ │ ┌────────────────────┐ │
│ │ │ Service │ │ │
│ │ └────────────────────┘ │
│ 2.2 │ │
│ │ ┌───────────────────────┐ │
│ └───▶│ Repository interface │ │ │
│ └───────────────────────┘ │
│ │ │ │
│ │ 3 │
│ └───────────────┼──┘
```
1. Send event using the bloc. The bloc does not know about the `Widget`. It should communicate through events.
2. The bloc dispatching the event to the appropriate handler of the `Domain`
1. Conversion between, DTO (Data Transfer Object) to domain model and Domain Model to DTO.
2. Handling the business logic using the repository interface
3. Repository are used to store the domain model data and calls respective service to finish the jobs.
# Event-Driven

12
doc/contribute.md Normal file
View File

@ -0,0 +1,12 @@
[WIP]
* Contributing
* First steps
* Opening issues
* Participating in discussions
* Finding something to work on
* Open your PR
* Review Process
* Getting more involved
* Organization membership
* Contributor
* Maintainer

243
doc/domain driven design.md Normal file
View File

@ -0,0 +1,243 @@
# 🍔 Domain Driven Design
For many architects, the process of data modeling is driven by intuition. However, there are well-formulated methodologies
for approaching it more formally. I recommend the [Domain-Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design)
and choose it as AppFlowy architecture.
## 💥 Layered architecture
The most common architecture pattern is the layered architecture pattern, known as the n-tier architecture pattern.
Partition the software into `layers` to reduce the complexity. Each layer of the layered architecture pattern has a
specific role and responsibility.`DDD` consists of four layers.
```
┌──────────────────────────────────────────────────┐ ─────────▶
│ Presentation Layer │──┐ Dependency
└──────────────────────────────────────────────────┘ │
│ │
▼ │
┌──────────────────────────────────────────────────┐ │
│ Application Layer │ │
└──────────────────────────────────────────────────┘ │
│ │
▼ │
┌──────────────────────────────────────────────────┐ │
│ Domain Layer │◀─┘
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ Infrastructure Layer │
└──────────────────────────────────────────────────┘
```
**Presentation Layer**:
* Responsible for presenting information to the user and interpreting user commands.
* Consists of Widgets and also the state of the Widgets.
**Application Layer**:
* Defines the jobs the software is supposed to do. (Shouldn't find any UI code or network code)
* Coordinates the application activity and delegates work to the next layer down.
* It doesn't contain any complex business logic but the basic validation on the user input before
passing to the other layer.
**Domain Layer**:
* Responsible for representing concepts of the business.
* Manages the business state or delegated to the infrastructure layer.
* Self contained and it doesn't depend on any other layers. Domain should be well isolated from the
other layers.
**Infrastructure Layer**:
* Provides generic technical capabilities that support the higher layers. It deals with APIs, persistence and network, etc.
* Implements the repository interface and hiding the complexity of the Domain layer.
As you see, the `Complexity` and `Abstraction` of these layers are depicted in this diagram. Software system are composed in layers,
where higher layers use the facilities provided by lower layers. Each layer provides a different abstraction from the layer above
and below it. As a developer, we should pull the complexity downwards. Simple interface and powerful implementation(Think about the
[open](https://man7.org/linux/man-pages/man2/open.2.html) function). Another way of expressing this idea is that it is more important
for a module to have a simple interface than a simple implementation.
```
Level of ├───────────────────┐
Abstraction │ Presentation │
├───────────────────┴───────┐
│ Application │
├───────────────────────────┴─────────┐
│ Domain │
├─────────────────────────────────────┴────────┐
│ Infrastructure │
└──────────────────────────────────────────────┴─────▶
Complexity
```
### Data Model
DDD classifies data as referenceable objects, or entities, and non-referenceable objects, or value objects. Let's introduces
some terminologies from DDD.
**Entity**
`Entities` are referenceable because they carry an identity which allows us to reference them. e.g. user, order, book, etc.
You can use `entities` to express your business model and encapsulate them into Factory that provides simple API interface
to create Entities.
**Value Object**
`Value Object` can't be referenced. They can be only included into entities and serve as attributes. Value objects could be
simple and treat as immutable. e.g. email, phone number, name, etc.
**Aggregate**
`Entity` or `Value object` can be grouped into aggregates. Aggregates can simplify the model by accessing the entire aggregate.
For instance, Table has lots of row. Each row using the table_id to reference to the
table. TableAggregate includes two entities: Table and the Row.
```
TableAggregate
┌────────────────────────────────────────────────────────────────┐
│ │
│ ┌────────────────────┐ ┌─────────────────────────┐ │
│ │struct Table { │ │struct Row { │ │
│ │ id: String, │ │ table_id: String, │ │
│ │ desc: String, │◀▶───────│} │ │
│ │} │ │ │ │
│ └────────────────────┘ └─────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────────┘
```
**Service**
When a significant process of transformation in the domain is not a natural responsibility of an `Entity` or `Value object`, add
an operation to the model as standalone interface declared as a Service. For instance: The `Value object`, EmailAddress,
uses the function `validateEmailAddress` to verify the email address is valid or not. `Service` exists in Application, Domain and
Infrastructure.
```
class EmailAddress {
final Either<Failure<String>, String> value;
factory EmailAddress(String? input) {
return EmailAddress._(
validateEmailAddress(input),
);
}
const EmailAddress._(this.value);
}
Either<Failure<String>, String> validateEmailAddress(String? input) {
...
}
```
**Repository**
Repository offer an interface to retrieve and persist aggregates and entities. They hide the database or network details from the domain.
The Repository interfaces are declared in the Domain Layer, but the repositories themselves are implemented in the Infrastructure Layer.
You can replace the interface implementation without impacting the domain layer. For instance:
```
// Interface:
abstract class AuthInterface {
...
}
// Implementation
class AuthRepository implements AuthInterface {
...
}
```
> More often than not, the repository interface can be divided into sub-repository in order to reduce the complexity.
### Relation
The diagram below is a navigational map. It shows the patterns that form the building blocks of Domain Driven Design and how they relate to each other.
![[image from here](http://uniknow.github.io/AgileDev/site/0.1.8-SNAPSHOT/parent/ddd/core/building_blocks_ddd.html)](imgs/domain_model_relation.png)
## 🔥 Operation Flow
```
presentation │ Application domain Infrastructure
│ │
7 Data Model
┌──────────────────────────────┐ │ ┌────────────────────────┐ │ ┌─────────────────────┐
│ │ │ │ ┌─────────────┐ │ │ Network Service │
▼ Bloc │ │ │ │ Aggregate │ │ │ └─────────────────────┘
┌─────────────┐ │ ┌─────────────────┴─────┐ │ └─────────────┘ │ ▲
────────▶ Widget │ │ ┌────────┐ ┌────────┐ │ 2 │ ┌────────┐ │ │ │ 6
└─────────────┘ │ │ │ Event │ │ State │ │────┬───▶│ │ Entity │ │ ┌─────────────────────┐
User │ │ └────────┘ └────────┘ │ │ │ │ └────────┘ │ │ │ Persistence Service │
interaction │ │ └──────▲────────────────┘ │ │ ┌─────────────────┐ │ └─────────────────────┘
│ │ │ │ │ │ Value Object │ │ │ ▲
└──────────┼────────┘ │ │ └─────────────────┘ │ │ 5
1 │ │ └────────────◈───────────┘ │ ┌─────────────────────┐
│ │ │contain │ Unit of Work │
│ │ ┌────────────────────┐ │ └─────────────────────┘
│ │ │ Service │ ▲
│ │ └────────────────────┘ │ │
│ │ │ 4
│ │ Repository │ │
│ │ ┌─────────────────────────────────────────────┴───────────────┐
│ │ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ 3 ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │
│ └───┤ Interface ────▶ Implementation │
│ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │
│ └─────────────────────────────────────────────────────────────┘
```
1. Widget accepts user interaction and transfers the interactions into specific events. The events will be send to the Application layer,
handled by the specific `bloc`. The `bloc` send the states changed by the events back to the widget, and finally the `Widget` update
the UI according to the state. The pattern is depicted in this diagram. (More about the flutter [bloc](https://bloclibrary.dev/#/coreconcepts?id=bloc))
```
┌──────────── State ────────────┐
│ │
▼ Bloc │
┌─────────────┐ ┌─────────────┼─────────┐
────────▶│ Widget │ │ ┌────────┐ ┌┴───────┐ │
└─────────────┘ │ │ Event │ │ State │ │
User interaction │ │ └────────┘ └────────┘ │
│ └───────────────────────┘
│ ▲
│ │
└────────── Event ────────────┘
```
2. The `bloc` process the events using the services provided by the `Domain` layer.
1. Convert DTO (Data Transfer Object) to domain model and Domain Model to DTO.
2. Domain model is the place where all your business logics, business validation and business behaviors will be implemented.
The Aggregate Roots, Entities and Value Objects will help to achieve the business logic.
3. Calling repositories to perform additional operations. The repositories interfaces are declared in `Domain`, implemented in `Infrastructure`.
You can reimplement the repository interface with different languages, such as `Rust`, `C++` or `Flutter`. etc.
```
Domain Infrastructure
Repository A │
┌────────────────────────────┼────────────────────────────────┐
│ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │
│ Interface ──┼─▶ Implementation │
│ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │
└────────────────────────────┼────────────────────────────────┘
Repository B │
┌────────────────────────────┼────────────────────────────────┐
│ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │
│ Interface ──┼─▶ Implementation │
│ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │
└────────────────────────────┴────────────────────────────────┘
```
4. Responsibility of [Unit of Work](https://martinfowler.com/eaaCatalog/unitOfWork.html) is to maintain a list of objects affected by a
business transaction and coordinates the writing out of changes and the resolution of concurrency problems((No intermediate state)).
If any one persistence service fails, the whole transaction will be failed so, roll back operation will be called to put the object
back in initial state.
5. Handling operations (INSERT, UPDATE and DELETE) with SQLite to persis the data.
6. Saving or querying the data in the cloud to finish the operation.

0
doc/getting_started.md Normal file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

1
doc/roadmap.md Normal file
View File

@ -0,0 +1 @@
[WIP]

View File

@ -0,0 +1 @@
[WIP]