Skip to content

Commit

Permalink
Merge pull request #567 from twin-te/develop
Browse files Browse the repository at this point in the history
クリーンアーキテクチャを導入
  • Loading branch information
hayato24s authored Sep 30, 2022
2 parents 7e4dd1c + 6c94913 commit f316baf
Show file tree
Hide file tree
Showing 330 changed files with 44,319 additions and 9,084 deletions.
File renamed without changes.
60 changes: 60 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Architecture

[実装クリーンアーキテクチャ](https://qiita.com/nrslib/items/a5f902c4defc83bd46b8)を踏まえて、twinte-front では以下のアーキテクチャは以下を採用しています。

![Architecture image](https://user-images.githubusercontent.com/68944024/161409877-375bb674-b668-4388-9df6-ddd081b9581f.png)

### 1. Entity

ビジネスロジックを表現するためのオブジェクトです。

### 2. UseCase

「このソフトウェアは何ができるのか?」を表現します。

### 3. IRepository

`UseCase``Port`を示します。`UseCase`を使用するためのルールのようなものです。

### 4. Repository

データの永続化を行います。`IRepository`の要件さえ満たしていれば、データベースでも、ローカルストレージでも実装方法は問いません。

### 5. Presenter

`ViewModel``Entity`の相互変換を行います。

### 6. ViewModel

UI のための型です。`View`で使用されます。

### 7. View

画面の描画を担当します。

# State Management

[Vue.js/State Management](https://vuejs.org/guide/scaling-up/state-management.html)を参考に twinte-front で採用している状態管理について解説します。

`View`を描画するためには、その元となる唯一の情報源が必要で、`State`と呼ばれています。また、ユーザーが`View`から何らかの入力をした場合に、`Action`が呼び出され、`State`を変更することができます。そして、`State`が変更されると`View`は更新されます。この単方向のデータフローを図に示すと以下のようになります。

![Data Flow](https://user-images.githubusercontent.com/68944024/161410858-c3dfca15-2645-4ec5-a7ae-14590462f8b7.png)

## Example

例えば、授業詳細画面でメモの更新を行うときは以下のような処理が行われます。

1. `State` をもとに `View` が描画されている
2. メモの更新を行うため `View` から `Action` を呼び出す(ex. `updateMemo`
3. `Action` から `UseCase` を呼び出す(ex. `updateRegisteredCrouse`
4. 更新された `ViewModel` を取得するため `Action` から `Presenter` を呼び出す(ex. `createViewModelCourse`
5. `State` を 4 で取得した `ViewModel` に更新する
6. `State` が変更されたため `View` が更新される

# References

- [実装クリーンアーキテクチャ](https://qiita.com/nrslib/items/a5f902c4defc83bd46b8)
- [Vue.js/State Management](https://vuejs.org/guide/scaling-up/state-management.html)
- [Twin:te backend v2](https://github.com/twin-te/twinte-server)
- [frontend-clean-architecture](https://github.com/bespoyasov/frontend-clean-architecture/tree/master/src)
- [flux](https://facebook.github.io/flux/docs/in-depth-overview)
7 changes: 5 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
/>
<meta
name="description"
content="筑波大学生専用の時間割アプリTwin:te(ついんて)です"
Expand All @@ -21,6 +24,6 @@
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="module" src="/src/ui/main.ts"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@gtm-support/vue-gtm": "^1.3.0",
"@sentry/browser": "^6.2.5",
"@sentry/tracing": "^6.2.5",
"@types/lodash": "^4.14.184",
"@types/qs": "^6.9.6",
"@unocss/reset": "^0.31.2",
"@vueuse/core": "^4.6.2",
Expand All @@ -30,6 +31,7 @@
"axios": "^0.21.1",
"dayjs": "^1.10.4",
"firebase": "^8.3.2",
"lodash": "^4.17.21",
"qs": "^6.10.1",
"vue": "^3.0.9",
"vue-router": "4",
Expand Down
61 changes: 0 additions & 61 deletions src/App.vue

This file was deleted.

48 changes: 38 additions & 10 deletions src/adapter/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,38 @@
import { ConfigType, Dayjs } from "dayjs";
import { Store } from "vuex";
import { ApiInstance } from "~/api/$api";
import { GlobalState } from "~/store";

export interface Ports {
api: ApiInstance;
store: Store<GlobalState>;
dayjs: (date?: ConfigType) => Dayjs;
}
import { Ports } from "~/application/ports";
import { CalendarRepositoryInMemory } from "~/repositories/development/CalendarRepositoryInMemory";
import { CourseRepositoryInMemory } from "~/repositories/development/CourseRepositoryInMemory";
import { FeedbackRepositoryInMemory } from "~/repositories/development/FeedbackRepositoryInMemory";
import { NewsRepositoryInMemory } from "~/repositories/development/NewsRepositoryInMemory";
import { UserRepositoryInMemory } from "~/repositories/development/UserRepositoryInMemory";
import { CalendarRepository } from "~/repositories/production/CalendarRepository";
import { CourseRepository } from "~/repositories/production/CourseRepository";
import { FeedbackRepository } from "~/repositories/production/FeedbackRepository";
import { NewsRepository } from "~/repositories/production/NewsRepository";
import { UserRepository } from "~/repositories/production/UserRepository";

const dev = false;
let ports: Ports | undefined = undefined;

export const usePorts = (): Ports => {
if (ports) return ports;

if (dev) {
ports = {
calendarRepository: new CalendarRepositoryInMemory(),
courseRepository: new CourseRepositoryInMemory(),
feedbackRepository: new FeedbackRepositoryInMemory(),
newsRepository: new NewsRepositoryInMemory(),
userRepository: new UserRepositoryInMemory(),
};
} else {
ports = {
calendarRepository: new CalendarRepository(),
courseRepository: new CourseRepository(),
feedbackRepository: new FeedbackRepository(),
newsRepository: new NewsRepository(),
userRepository: new UserRepository(),
};
}

return ports;
};
Loading

0 comments on commit f316baf

Please sign in to comment.