Slide 1

Slide 1 text

Angular Webアプリケーション 最新設計手法 HTML5 Conference 2018

Slide 2

Slide 2 text

2 @kou

Slide 3

Slide 3 text

3 お話すること Component Design Module Structure State Management

Slide 4

Slide 4 text

4 設計 拡張性 保守性 信頼性 - 設計とは、アプリケーションの柔軟性や堅牢性の基盤 - 設計とは、開発者同士の実装に対する共通意識 - 良い設計は、アプリケーションの信頼性と拡張性、開発効率の向上を生み出す なぜ設計を考える必要があるのか ?

Slide 5

Slide 5 text

5 Angular - Web Applicationのためのフレームワーク - TypeScript / One Frameworkの堅牢なアプリケーション基盤 - Component - based Architecture

Slide 6

Slide 6 text

6 Component - based Architecture アプリケーションそのものが 1つの Component で表現される Application is Component Application Component =

Slide 7

Slide 7 text

7 Component - based Architecture ...

Slide 8

Slide 8 text

8 Component - based Architecture App Component Component Component Component Component Component - 最上位に AppComponentがあり、その下に子 Component がツリー構造で存在 - アプリケーションのそれぞれの機能は 1つ1つの Component で表現される

Slide 9

Slide 9 text

9

Slide 10

Slide 10 text

10 App Component

Slide 11

Slide 11 text

11 Component

Slide 12

Slide 12 text

12 Component

Slide 13

Slide 13 text

13 Component

Slide 14

Slide 14 text

- アプリケーションは Component で表現される - アプリケーション設計とは、Componentの分類と設計の手法と言える - 良い Component 設計さえあれば、複雑な設計は必要でない 14 Component Component Design How should we design Components ?

Slide 15

Slide 15 text

Component Design

Slide 16

Slide 16 text

16 Component の分類 - Component 設計の第一歩は、適切な分類分けをすること - Component は役割に応じて4種類に分類することができる App Component Page Component Container Component Presentational Component Component Design

Slide 17

Slide 17 text

17 Component の分類 App Component Page Component Container Component Presentational Component Component Design Application そのものを表す、トップレベルの Component Web Pageを表す、URL単位の Component Serviceを使用して状態管理を行う Component 状態を持たない、表示専用の Component

Slide 18

Slide 18 text

18 App Component

Slide 19

Slide 19 text

19 Page Component

Slide 20

Slide 20 text

20 Container Component

Slide 21

Slide 21 text

21 Presentational Component

Slide 22

Slide 22 text

App Component

Slide 23

Slide 23 text

23 App Component

Slide 24

Slide 24 text

24 App Component Application そのものを表す、トップレベルの Component @Component({ selector: 'app-root', template: ` `, }) export class AppComponent implements OnInit { ngOnInit(): void { // アプリケーション初期化処理など } } Component Design

Slide 25

Slide 25 text

25 App Component How should we use App Component ? - アプリケーションの全体の設定や、各 Serviceの初期化処理などを行う - 例)APIからの初期データの取得、WebSocketの接続処理など - 初期化処理は基本的に Service に移譲し、シンプルに保つ Component Design

Slide 26

Slide 26 text

Page Component

Slide 27

Slide 27 text

27 Page Component

Slide 28

Slide 28 text

Page Component

Slide 29

Slide 29 text

29 Page Component @Component({ template: ` `, }) export class PageComponent implements OnInit { constructor( private route: ActivatedRoute, ) {} ngOnInit(): void { // RouterからURL情報を取得し、Componentの状態を設定 this.heroId = this.route.paramMap... } } Web Pageを表す、URL単位の Component Component Design

Slide 30

Slide 30 text

30 Page Component - 1 URLにつき、1 Page Componentを作成する ( 再利用はしない ) - テンプレートは Container Component、Presentational Componentで構成する - Router、ActivatedRoute などの Router関連の機能はこの Componentでのみ使用する Component Design How should we use Page Component ?

Slide 31

Slide 31 text

Container Component

Slide 32

Slide 32 text

32 Container Component

Slide 33

Slide 33 text

33 Container Component @Component({ selector: 'app-heroes', template: ` `, }) export class HeroesComponent implements OnInit { constructor(private heroService: HeroService) {} ngOnInit(): void { // Serviceを使用してComponent内のデータを初期化 this.heroes = this.heroService.getHeroes()... } } Serviceを使用して状態管理を行う Component Component Design

Slide 34

Slide 34 text

34 Container Component - Service と Presentational Component のデータの受け渡しに専念し、ロジックを持たせない - 表示については Presentational Component を使用し、この Componentでは行わない - テストが不要になるレベルでシンプルに保つ Component Design How should we use Container Component ?

Slide 35

Slide 35 text

Presentational Component

Slide 36

Slide 36 text

36 Presentational Component

Slide 37

Slide 37 text

37 Presentational Component @Component({ selector: 'app-hero-detail', template: `
{{ hero.name }}
Delete `, }) export class HeroesComponent implements OnInit { @Input() hero: Hero; @Output() buttonClick = new EventEmitter(); } 状態を持たない、表示専用の Component Component Design

Slide 38

Slide 38 text

38 Presentational Component - シンプルな HTML のみで構成する - Serviceを使用せず、@input のみを使用してデータを受け取る - テンプレート内のイベントは @Output を使用して上位 Component へ伝播させる - この Component でアプリケーションの大部分を構成するようにする Component Design How should we use Presentational Component ?

Slide 39

Slide 39 text

39 Component の分類のコツ - Component の分割は、開発規模に応じて柔軟に行う - 例え冗長であっても、適切に分割することが重要 - 表示専用の Presentational Component をなるべく多く作成する - Container Component が複雑にならないよう、Serviceに処理を寄せる Component Design App Component Page Component Container Component Presentational Component

Slide 40

Slide 40 text

40 Component のテスト粒度 App Component Page Component Container Component Presentational Component Component Design E2Eテストでページ表示のテストを行う E2Eテストでページ表示のテストを行う Serviceに対してのメソッドコールをユニットテストで確認する @Input、@Outputが適切に処理されているかユニットテストを行う

Slide 41

Slide 41 text

Module Structure

Slide 42

Slide 42 text

42 Module - Module は 機能単位での Component の集合 - Service なども含めることができるが、アプリケーション用の Module は Component 群のカプセル化と考えるのが良い Module Structure Module is a set of Components has Module Components

Slide 43

Slide 43 text

43 @NgModule({ imports: [ CommonModule, MaterialModule, ... ], declarations: [ HeroPageComponent, HeroListComponent, HeroComponent, ], }) export class HeroModule {} Module Module Structure

Slide 44

Slide 44 text

44 Module App Module Feature Module Feature Module Feature Module Feature Module Feature Module Module 設計 - Moduleは Component と同様に、ツリー構造を持つ - Module はそれだけで完結する機能の単位として分割する - App Module の子となる機能単位でカプセル化された Module のことを Feature Module と呼ぶ Module Structure

Slide 45

Slide 45 text

45 Module - 小規模なアプリケーションではルートとなる AppModule だけで十分 - 大規模な開発では、機能毎に開発担当者が異なるため Feature Module が必要 - ( 一部の変更が他の Module に影響を与えないためのカプセル化 ) Module 設計 Module Structure Why should we use Module ?

Slide 46

Slide 46 text

46 Core & Shared Module - Angular の公式で紹介されている特殊な Module - これらの Module は複数の Feature Module で共有して使用される - Core Module は Serviceや関数を、Shared Module は Componentや Pipe を管理する Core Module Shared Module Module Structure

Slide 47

Slide 47 text

47 Core & Shared Module - Core Module は Service などのロジックに関する共通の機能を提供 - Shared Module は Component などの View に関する共通の機能を提供 - 全ての Feature Module はこの 2つの Module を共有して使用する Module Structure

Slide 48

Slide 48 text

48 Feature ModuleとComponentのディレクトリ構造 - Feature Module は feature-modules というフォルダに配置 - Feature Module 内に機能単位でサブフォルダを作成 - Feature Moduleは、他の Feature Module に影響を与えない (1部のFeature Moduleが壊れても他の箇所は動作する) Module Structure

Slide 49

Slide 49 text

State Management

Slide 50

Slide 50 text

50 State Management State Management - State とは、移り変わるアプリケーションの状態を表す - State Management とは アプリケーションのデータ管理方法 - Angularでは、主に Component と Service の連携方法 Component Component State Service

Slide 51

Slide 51 text

51 2種類の State Management Component Single Data Service Data Service Global Store Store Data Service Data Service Component Component Component Component Component State Management

Slide 52

Slide 52 text

Single Data Service

Slide 53

Slide 53 text

53 Single Data Service - 1つのデータタイプ (モデル) につき、1つの Serviceを作成し状態管理する - Service はデータを Stream として提供し、 Component はそれらを subscribe する形で、リアルタイムにデータを受け取る Data Service Component Component update subscribe State Management

Slide 54

Slide 54 text

54 Single Data Service @Injectable({ providedIn: 'root', }) export class HeroDataService { private _data$ = new BehaviorSubject([]); getData(): Observable { return this._data$.asObservable(); } update(data: Hero[]): void { this._data$.next(data); } } @Component({ ... }) export class HeroListComponent implements OnInit { private heroes$: Observable; constructor( private heroDataService: HeroDataService, ) {} ngOnInit(): void { this.heroes$ = this.heroDataService.getData(); // データの種類に対して 1つのDataServiceを使う // this.villains$ = this.villainDataService.data$; } } State Management

Slide 55

Slide 55 text

55 Single Data Service Data Service Component Component - 小さく始められる - シンプルで仕様理解が簡単 Pros - Serviceの数が増えた時に急激に複雑化 - どのデータがどの Service にあるのか分かりづらい - 各 Service毎に仕様が異なる可能性がある Cons update subscribe State Management

Slide 56

Slide 56 text

56 Single Data Service - 小規模なアプリケーション (1-3人程度の開発者) - 仕様が開発と同時に進むような、スピードの求められる開発 - ある程度の大規模なアプリケーションが予定される場合は、 後述の Global Storeを使用すべき State Management When should we use Single Data Service ?

Slide 57

Slide 57 text

Global Store

Slide 58

Slide 58 text

58 Global Store - 1つの Store が全てのデータを管理 (Databaseとなる) - Component は Actionを使用して Store を更新 - Data (State) の更新処理には reducer という関数を使用 Store reducer dispatch Action subscribe Component Component State Management

Slide 59

Slide 59 text

59 Global Store NGRX Reactive State for Angular State Management ngrx.io

Slide 60

Slide 60 text

60 Global Store - 決まった形式での統一的な実装 - Storeの拡張性が高く、大規模な状 態管理に強い Pros - 小規模なアプリケーションに対してはやや複雑 - どの Action がどの状態を変更するか分かりづらい Cons State Management Store reducer subscribe Component Component dispatch Action

Slide 61

Slide 61 text

61 Global Store - 大規模なアプリケーション (5人以上の開発者) - 適切な Action 設計を考えてアプリケーション開発が可能な開発チーム State Management When should we use Global Store ?

Slide 62

Slide 62 text

Action Design

Slide 63

Slide 63 text

63 Action Design - Data Service と違い、Store はアプリケーションの中央 Database そのもの - 直接 Database の書き換えができないように、 Store の直接の更新はできない - Action / Reducer は、Database に対しての更新クエリと見なせる State Management Why should we use Action ?

Slide 64

Slide 64 text

64 Action Design - Actionはアプリケーションの状態変更を示す - 分かりやすい Actionの設計は、アプリケーション仕様の理解を簡単にする - Global Store を使う上で、Actionの設計が最も重要になる State Management How should we use Action ?

Slide 65

Slide 65 text

65 Action Design Login Load Users Update User Detail Load User Detail Reload Users Login後、User一覧ページから指定のUser情報を閲覧、アップデートする一連の処理 State Management

Slide 66

Slide 66 text

66 Action Design Login後、User一覧ページから指定のUser情報を閲覧、アップデートする一連の処理 [ Login Page ] Login [ User List Page ] Load Users [ User Detail Page ] Update User Detail [ User Detail Page ] Load User Detail [ User List Page ] Reload Users Login Load Users Update User Detail Load User Detail Reload Users State Management

Slide 67

Slide 67 text

67 Action Design Login後、User一覧ページから指定のUser情報を閲覧、アップデートする一連の処理 [ Login Page ] Login [ User List Page ] Load Users [ User Detail Page ] Update User Detail [ User Detail Page ] Load User Detail [ User List Page ] Reload Users [ Login API ] Login Success [ Router ] Move Page [ User API ] Load Users Success [ User API ] Load User Detail Success [ Router ] Move Page [ User API ] Update User Detail Success [ User API ] Reload Users Success [ Router ] Move Page State Management

Slide 68

Slide 68 text

Conclusion

Slide 69

Slide 69 text

69 Component Design Module Structure State Management Conclusion - Component 分割を適切に行う - 表示に重点を置き、ロジック をシンプルにすること - 機能、URL単位で適切に分ける - 他機能に影響を与えないように カプセル化を利用する - アプリケーション、開発チームの 規模に応じた適切な状態管理を 採用する

Slide 70

Slide 70 text

ありがとうございました