Nest.jsとSapperを組みわせて快適プロトタイピング環境を作ってみた

## 背景：
色々と巷で良いと言われているフロントエンドフレームワークを色々仕事で使った事があるのですが（Angular>React>Vue)、テストやプロトタイピング的な案件では、去年から全てSapperで作っています。

Svelteはシンプルで記述量も少なく、コードが読み易く再利用がしやすいのでとても気に入っていますが、バックエンド搭載でSSRにも対応しているSapperの方が便利なので普段はこちらを使います。

ただ、Sapperのバックエンドは癖が強く再利用がしにくいので、バックエンドに関しては、スピード感のある開発ができて本番運用もし易いNest.jsを使いたい。（結局は、好みなんですが）。

色々書いたのですが、主な要望は以下です。
 - 型に関しては共通で書きたい！（将来のために！）
 - RESTやDBなどの通信系が最初のウチは雑に書ける。（あまり時間を割きたくない）
 - 一度作ったものが他の箇所で使用し易い。
 - Front+Back系プロジェクト特有のゴチャつきを防ぎたい

説明中、用語の使い方や表現のゆらぎが気になる方は、下の記事を読むと多分頭が少しスッキリします。
ー[お前らがModelと呼ぶアレをなんと呼ぶべきか。近辺の用語(EntityとかVOとかDTOとか)について整理しつつ考える](https://qiita.com/takasek/items/70ab5a61756ee620aee6#%E3%82%A2%E3%83%AC%E3%82%92model%E3%81%A8%E5%91%BC%E3%81%B6%E3%81%A8%E4%BD%95%E3%81%8C%E4%B8%8D%E5%91%B3%E3%81%84%E3%81%AE%E3%81%8B)

「リンク早よっ！」て方は、こちらから：[github: Nest.js, Sapper with Clean Archtecture: Working Example](https://github.com/walker-walks/nest.js-sapper-example)


## Framework構成
基本的に、nestをexpressで設定すると、sapperのミドルウェア使えるので設定しただけです。
秘密はそれだけ。

```typescript
async function bootstrap() {
	const app = await NestFactory.create(ServerModule);

	app.setGlobalPrefix(endpoints.prefix); // 干渉を避けるためGlobalPrefix="api"
	app.use(
		compression({ threshold: 0 }),
		sirv('static', { dev }), // SSRの要？
		sapper.middleware({ignore: /^\/api(.*)/}), // # /apiから始まるpathには適応しない。
	);

	await app.listen(PORT || 3000);
}
bootstrap();
```


## フォルダ構成

プロトタイピングとは言っても、案外MVPまでプロジェクトが進行してしまう事が良くあるので、ある程度本番でも、使える様にしておきます。

基本的には、"**_[クリーンアーキテクチャの文脈](https://qiita.com/takasek/items/70ab5a61756ee620aee6#%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E6%96%87%E8%84%88)_**"にそいつつ、使いやすさを考えながら組んでいます。ただ、*1ゴリゴリの実装は返って書く量が増えてしまうので、今回はみんな馴染みのあるMVCとDDDの風味を残しておきます。

*1. [実装クリーンアーキテクチャ](https://qiita.com/nrslib/items/a5f902c4defc83bd46b8)
![CleanArchitecture.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41072/b726b269-9b1e-45fc-0976-ea5c32c28a6e.jpeg)


### /domain: Entity

早速です、DDDならDomain、クリーンアーキテクチャならEntityに当たります。
名前はまちまちですが、考え方的には、次のapplication(Use Case)層で扱うときに、下の図くらいドメインモデルが抽象化されていると理想です。
> ![](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F393000%2F3a998539-bc33-9683-9abc-848fa076e3d5.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=06ebc3e6d7ea4bef481490661ef8085b)
>ref: [関心事に相応しい命名をする](https://qiita.com/MinoDriven/items/37599172b2cd27c38a33#%E9%96%A2%E5%BF%83%E4%BA%8B%E3%81%AB%E7%9B%B8%E5%BF%9C%E3%81%97%E3%81%84%E5%91%BD%E5%90%8D%E3%82%92%E3%81%99%E3%82%8B)

どういう事か、
図で言う。予約品(aggregate)はデータベースやシステムを見ると、顧客情報と予約情報と商品情報の集合です。
ドメインのRepositoryでは予約品という、システムから見て抽象化された業務で使う単位で、保存、読み出しできる事が理想です。

なので、分け方としては、以下のようにします。

```terminal
domain/repository/yoyaku-hin.repository.ts
domain/model/yoyaku-hin.model.ts
domain/entity/kokyaku.entity.ts
domain/entity/yoyaku.entity.ts
domain/entity/shouhin.entity.ts
```
このようにrepositoryがaggregateをしているため、ここでCRUDを書いておくと、最初はControllerに直結して使う事ができます。
ここで、以下の様に雑でも、汎用なスーパークラスを作っておくと、ミスが減る上に、かなり時間が節約。
DDDでは、yoyaku-hin.modelも「一意な識別子によって特定される」ため、本当はEntityなのですが、ややこしいので
O/Rマッピングの対象を.entity.tsに書き、ドメインモデルを.model.tsに書いています。

```typescript
// #shared/pattern/repository-controller.class.ts
export class RepositoryController<T> implements IRepositoryProxy {
  constructor(
    private readonly repository: IReposiotry
  ) { }

  @Get()
  findAll(@Query() filter) {
    return this.repository.findAll(filter);
  }

  @Get(':id')
  findById(@Param('id') id, @Query() filter) {
    return this.repository.findAll(id, filter);
  }

  @Post()
  create(@Body() body, @AuthUser() user) {
    return this.repository.create(user, body);
  }
}

// #interface/controller/yoyaku.controller.ts
import { endpoints } from 'interface/api/api.conf';

@Controller(endpoints.paths.yoyaku)
@UseGuards(AuthGuard())
export class YoyakuController extends RepositoryController<YoyakuHin>{

  constructor(
    yoyakuRepo: YoyakuRepository
  ) {
    super(yoyakuRepo);
  }
  
}

```

若干バカバカしく見えるかも知れないけど、コイツが結構便利で、プロジェクトが進行をしていっても、
成長するに連れてブラッシュアップしていったり、いくつかバージョンを作ると最後まで生き残ったりします。
ただ基本的には、Mock向けで複数人いる場合とりあえず繋げて、フロントエンドを開発し易くする事ができます。


### /application: Use Case

RepositoryとAuthGuardとSuperControlerのコンボは強力で、一見「もう、やる事ねーじゃん。Application層、イラネーじゃん」となりそうですが。ここからが、本番です。
例えば、単純に予約品を登録(POST)するにしても、実際にはデータの保存だけでなく、認証や権限を確認したり、お客さんにメールを送ったりと沢山する事があります。
ここでもやっぱり、IRepositoryProxyを実装したSuperServiceを基本的には継承し、必要な箇所を上書きしていくと、時間が短縮できます。

```typescript
@Injectable()
export class YoyakuService extends RepositoryService<YoyakuHin> {

  constructor(
    private readonly yoyakuRepo: YoyakuRepository,
    private readonly smsService: SmsService,
  ) {
    super(yoyakuRepo);
  }

  // 例：オリジナルを上書きし、デフォルト値を設定する
  findAll(_filter): Promise<YoyakuHin[]> {
    const filter = Object.assign({default: false}, _filter);
    return this.yoyakuRepo.findAll(filter);
  }

  // 例：オリジナルを上書きし、保存後にSMSを送信する。
  async create(body, user): Promise<YoyakuHin> {
    const yoyakuHin = await this.yoyakuRepo.creat(body, user);
    this.smsService.sendSendSMS(yoyakuHin);
    return yoyakuHin;
  }
  
}
```


### /interface: Controller, Adapter

ここでは、システム間のインターフェースを設定します。
サーバのControllerとクライアントのService(Angular的?)に当たりますが、名前がService被りしている上に、application/serviceにきちんとロジック書いているなら、コイツの役目は基本にサーバーに繋ぐだけ。それ以外に変なことはして欲しくないので、Adapterに名前変更。
AuthentecatedAdapterとPublicAdapterぽいスーパークラス作り、各pathを変数に入れることで、ほぼコピペで済み、ミスが格段に減ります。
あとは、ここにDev/Hom/Prodのホスト名や各pathを書いておくと超便利。

```typescript
// #interface/api/api.conf.ts
export const endpoints = {
  prefix: `api`,
  paths: {
    yoyaku: `yoyaku-hin`
  }
}

// #interface/adapter/yoyaku.adapter.ts
import { endpoints } from 'interface/api/api.conf';

export class YoyakuHinAdapter extends AuthentecatedAdapter<YoyakuHin> {
  constructor(){
    super(endpoints.paths.yoyaku);
  }
}

// #interface/controller/yoyaku.controller.ts
import { endpoints } from 'interface/api/api.conf';

@Controller(endpoints.paths.yoyaku)
@UseGuards(AuthGuard())
export class YoyakuController extends RepositoryController<YoyakuHin>{

  constructor(yoyakuRepo: YoyakuRepository) {
    super(yoyakuRepo);
  }
  
}
```

### /infrastructure: 設定・インフラ
このにはサーバーやデータベースの接続設定、データベースのコネクターは/domain/*.entity.tsを読み込んで管理する。
以下の様にすると、オニオンアーキテクチャの様にデータベースが、Entityに依存する仕様に作れる。

```typescript
// #/infrastructure/sql.conn.ts
import { YoyakuEntiy, KokyakuEntity, ShouHinEntity } from 'domain/model';
import { connect } from 'some-db-connector'

export const connection = connect({
  models: [YoyakuEntiy, KokyakuEntity, ShouHinEntity]
});

```

### /components: UI
Svelteのコンポーネントを記述し行きます。何のことはないですね、
componentsかuiか悩みましたが、馴染みがある方にしました。

### /routes: Web
Sapperの構造上、コイツがウェブのルートに対応しています。これはもう好きに呼んで下さい。


### 試したい点
仕組みを調べる時間がなかったので、SSRの機構をそのままにしてあります。これをNestのモノに置き換えれるのか、また、パーフォーマンスが上がるのかが気になります。

## やり残した事
プロトタイピングが主な使い方なので、ユニットテストの設定を移植してません（雑）。
storybookとか入れたらいい感じのボイラープレートになるかも知れんです。
後READMEとか、ちゃんと書きたいところです。


## 後書き
オニオンアーキテクチャで組んだのは、初めてなので、間違っていたり。改善できる点があれば、教えてくれるとありがたいです。


プロジェクト: [github: Nest.js, Sapper with Clean Archtecture: Working Example](https://github.com/walker-walks/nest.js-sapper-example)