Slide 1

Slide 1 text

͓Εͨͪͷ εϚϗΞϓϦͱ Web൛ Real World React Native in Agri/Med/Fin Tech

Slide 2

Slide 2 text

ࠓ೔͸ͳ͢͜ͱ • ͓Εͨͪͱ͓ΕͨͪͷεϚϗΞϓϦʹ͍ͭͯ • εϚϗΞϓϦͰϛεͬͨ͜ͱ • Web൛Ͱ΍Δઃܭ

Slide 3

Slide 3 text

͓Ε͸ (͓·͑͸?)

Slide 4

Slide 4 text

moqada

Slide 5

Slide 5 text

͓Εͨͪ͸

Slide 6

Slide 6 text

Kanmu

Slide 7

Slide 7 text

ͭͬͯ͘Δ΋ͷ

Slide 8

Slide 8 text

όϯυϧΧʔυ • ΞϓϦ͔Β1෼ͰൃߦͰ͖ΔVISAϓϦϖΠυΧʔυ • બ΂ΔόʔνϟϧΧʔυͱϦΞϧΧʔυ • https://vandle.jp

Slide 9

Slide 9 text

όϯυϧΧʔυͷྺ࢙ • 2015೥12݄ ։ൃ։࢝ • ωΠςΟϒܦݧऀͷ͍ͳ͍νʔϜͰૉૣͦ͘ΕͳΓͷΫΦϦςΟͰϦϦʔε͢ΔͨΊʹ React Native Λબ୒ (౰ॳ2016೥5݄ϦϦʔε༧ఆ) • 3ਓͷ։ൃνʔϜͰɺFintechࣄۀΛͲ͏࡞Δʁ࠷খߏ੒ͷνʔϜͰ௅Ήɺͦͷઓུͱ͸ (https:// seleck.cc/850) • React Native Ͱ͸iOS൛ͷΈ࣮૷͢Δ༧ఆͩͬͨ • 2016೥9݄ iOS൛ϦϦʔε • 2016೥12݄ Android൛ϦϦʔε • ҙ֎ͱAndroid΋͙͍͚ͨ͢ • 2017೥9݄ Web൛ϦϦʔε༧ఆ

Slide 10

Slide 10 text

όϯυϧΧʔυͷٕज़ελοΫ • ϑϩϯτ͸ɺReact Native, Redux, Flow, ESLint, ESDoc, etc... • όοΫΤϯυ͸Golang • JSON Hyper Schema Ͱ APIϦιʔεͷܕΛڞ ༗ͯ͠ΫϥΠΞϯτΛࣗಈੜ੒

Slide 11

Slide 11 text

Α͛͞ײͷ͋ΔελοΫ (2016౰࣌)

Slide 12

Slide 12 text

͚ͩͲݱ࣮͸ͭΒ͍

Slide 13

Slide 13 text

͓Εͨͪͷࣦഊ • ΞʔΩςΫνϟ͕݁ߏόϥόϥ • ౰࣌ͷ։ൃਞ(ideyuta, moqada)͕ٸਐ೿ͩͬͨ͜ͱ΋͋Γ࠷ద ͳΞʔΩςΫνϟΛٻΊͨ݁Ռɺ࣌ظຖʹඍົʹ࣮૷͕ҧ͏ঢ় گʹ • ͋ͱͰ౷Ұ͠Α͏ͱ͍͕ͯͨ͠ɺ͕࣌ؒͱΕͣʹϦϦʔεʹࢸΔ • ϦϦʔεޙɺͦͷ্ʹػೳ௥ՃΛ͍ͯ͘͜͠ͱʹͳΓͭΒ͍ίʔυ ͕Ճ଎ • ౰ॳͱΖ͏ͱࢥͬͯͨϦϑΝΫλͷ͕࣌ؒͱΕͣ

Slide 14

Slide 14 text

݁Ռ

Slide 15

Slide 15 text

Ͳ͜ͰԿ͕ى͍ͬͯ͜Δͷ͔ ॲཧ͕௥͍ʹ͘͘ɺ มߋͮ͠Β͍ίʔυʹ

Slide 16

Slide 16 text

ࠓճͷ͓୊

Slide 17

Slide 17 text

ॲཧ͕௥͍ʹ͍͘໰୊

Slide 18

Slide 18 text

ʮॲཧʯͱ͸ • UIΛؚΉΞϓϦέʔγϣϯͷಈ͖ • ྫ: ϩάΠϯϘλϯԡͨ͠ΒͲ͏ͳΔͷɺͲͷ ϖʔδʹભҠ͢Δͷ? • σʔλͷ૊Έ߹ΘͤʹΑΔৼΔ෣͍ • ྫ: Կ͕Ͳ͏૊Έ߹ΘͤͬͨΒܯࠂঢ়ଶʹͳΔͷ?

Slide 19

Slide 19 text

ʮॲཧʯͱ͸ • UIΛؚΉΞϓϦέʔγϣϯͷಈ͖ • ྫ: ϩάΠϯϘλϯԡͨ͠ΒͲ͏ͳΔͷɺͲͷ ϖʔδʹભҠ͢Δͷ? • σʔλͷ૊Έ߹ΘͤʹΑΔৼΔ෣͍ • ྫ: Կ͕Ͳ͏૊Έ߹ΘͤͬͨΒܯࠂঢ়ଶʹͳΔͷ?

Slide 20

Slide 20 text

Θ͔Γʹ͍͘ʮUIΛؚΉΞϓϦ έʔγϣϯͷಈ͖ʯ • ྫ͑͹ಛఆͷॲཧΛ଴ͬͯϖʔδભҠͤ͞Δ৔߹ • ͍ΖΜͳύλʔϯͰॻ͚Δ • ReactϥΠϑαΠΫϧϝιουύλʔϯ • ActionCreatorύλʔϯ • Sagaύλʔϯ

Slide 21

Slide 21 text

ReactϥΠϑαΠΫϧϝιου ύλʔϯ • componentWillReceivePropsͰ໨తͷΦϒ δΣΫτ͕౉͖ͬͯͨΒભҠͤ͞Δύλʔϯ • ͔ͳΓ΍͹͍΍ͭ

Slide 22

Slide 22 text

ReactϥΠϑαΠΫϧϝιουύ λʔϯ class HogeAuthRequestScene extends React.Component { componentWillReceiveProps(nextProps: Props) { // ಡΈࠐΈதͷ৔߹ͷΈ if (this.state.isLoading) { // ʮrequestͷঢ়ଶ͕มΘ͍ͬͯΔ == API͔Β݁ՌΛड͚औͬͨʯͱղऍ͢Δ if (!isEqual(props.request, this.props.request)) { // Πϯδέʔλ͕มԽ͠ऴΘΔͷΛ଴ͬͯNavigationΛભҠ͢Δ this.changeIndicator(indicatorType, () => { this.props.navigator.props.push(getFooRoute()); }); } } } }

Slide 23

Slide 23 text

ActionCreatorύλʔϯ • ActionCreatorʹඇಉظॲཧΛू໿࣮ͯ͠ߦ͢Δ ύλʔϯ • redux-thunk΍redux-promiseͱ͔Λ࢖͏৔߹͕ ͋Δ • ReduxͷActionʹີ݁߹͔ͭඇಉظͳͷͰςε τ͠ʹ͍͘

Slide 24

Slide 24 text

ActionCreatorύλʔϯ export function fooRequest(foo: string, bar: string): AsyncAction { return async (dispatch, getState) => { // ΠϯδέʔλΛදࣔ dispatch(indicatorShow()); const state = getState(); // APIϦΫΤετΛ౤͛Δ const action = await dispatch(apiActions.request({bar, foo})); // ΠδέʔλΛඇදࣔ dispatch(indicatorHide()); await saveRequest(action.payload); // φϏήʔγϣϯͰτοϓʹ໭Δ dispatch(navigationActions.popToTop(NAVIGATOR.app)); return action; }; }

Slide 25

Slide 25 text

Sagaύλʔϯ • redux-sagaʹҰ࿈ͷॲཧΛू໿࣮ͯ͠ߦ͢Δ ύλʔϯ • खଓ͖తʹ͔͚ͯΘ͔Γ΍͘͢͸͋Δ • GeneratorͰͻͨ͢Βॻ͍͍ͯ͘ɺܕ͕ࢦఆ͠ ʹ͘ҹ৅

Slide 26

Slide 26 text

Sagaύλʔϯ export function *fooSaga(): Generator { yield fork(function *saga() { // ϦΫΤετΠϕϯτΛ଴ͭ yield* takeLatest(types.FOO_REQUEST, receiveRequest); }); } function *receiveRequest(action: RequestAction) { const state = yield select(s => s.navigation); // Πϯδέʔλදࣔ yield fork(showIndicator); yield put(requestActions.foo(action.payload)); const receiveAction = yield take(types.FOO_REQUEST_RECEIVE); // Πϯδέʔλඇදࣔ yield take(hideIndicator); if (state) { // φϏήʔγϣϯΛ໭Δ yield put(navigationActions.pop()); } }

Slide 27

Slide 27 text

ͬ͘͠Γ͜ͳ͍

Slide 28

Slide 28 text

ͬ͘͠Γ͜ͳ͍ • Component͸͕͢͞ʹ·͍ͣ • Saga͸Αͦ͞͏͚ͩͲܕ͕ඍົͳ఺͕͍·ͻ ͱͭ • ͜ΕΒͷํ๏͕ࠞࡏͯͨ͠Γ͢Δͱ΋͏…

Slide 29

Slide 29 text

ʮॲཧʯͱ͸ • UIΛؚΉΞϓϦέʔγϣϯͷಈ͖ • ྫ: ϩάΠϯϘλϯԡͨ͠ΒͲ͏ͳΔͷɺͲͷ ϖʔδʹભҠ͢Δͷ? • σʔλͷ૊Έ߹ΘͤʹΑΔৼΔ෣͍ • ྫ: Կ͕Ͳ͏૊Έ߹ΘͤͬͨΒܯࠂঢ়ଶʹͳΔͷ?

Slide 30

Slide 30 text

Θ͔Γʹ͍͘ʮ૊Έ߹ΘͤʹΑ ΔৼΔ෣͍ʯ • ྫ͑͹ϢʔβͱΧʔυ৘ใ͔ΒܯࠂεςʔλεΛಋग़͢Δ৔߹ • ؾΛൈ͘ͱ͍ΖΜͳ৔ॴʹॻ͍ͪΌ͏͜ͱ͕͋Δ • Selectorύλʔϯ • Selector(connect௚ॻ͖)ύλʔϯ • Helperύλʔϯ • Comonent಺Ͱܭࢉύλʔϯ

Slide 31

Slide 31 text

͔͋Μ

Slide 32

Slide 32 text

͔͋Μ • ͪΌΜͱॻ͘৔ॴΛܾΊΑ͏… • ֓೦ΛϞσϧԽͯ͠ɺͦͷϝιου౳ʹ͢Δ • ౰ͨΓલͰ͕͢…

Slide 33

Slide 33 text

ղܾͷͨΊͷΞϓϩʔν • UIΛؚΉΞϓϦέʔγϣϯͷಈ͖ • Ϣʔεέʔε૚Λͭ͘Δ͜ͱͰγφϦΦΛखଓతʹهड़͢Δ • Clean Architecture ͱ͔ͷ΍ͭ • σʔλͷ૊Έ߹ΘͤʹΑΔৼΔ෣͍ • υϝΠϯ૚Λͭ͘Δ͜ͱͰ࢓༷ΛہॴԽ͢Δ • DDDతͳ࿩

Slide 34

Slide 34 text

࣮ࡍʹͲ͏΍Δ͔

Slide 35

Slide 35 text

Ϣʔεέʔε૚

Slide 36

Slide 36 text

Ϣʔεέʔε૚ͷ࢓૊Έ • खଓ͖͸UseCaseΫϥεʹ·ͱΊΔ • Container͔Β͸UseCase༻ͷ Command(Action)Λൃߦ͠SagaͰॲཧ͢Δ • ࢖͏ଆ͕ҙࣝ͢Δͷ͸ContainerͱUseCaseͩ ͚

Slide 37

Slide 37 text

UseCaseΫϥε type Prams = {username: string, password: string}; class LogInUseCase extends UseCase { static create() { const authServiceProvider = AuthServiceProvider.create(); return new LogInUseCase({ authService: new AuthService({provider: authServiceProvider}), navigation: Navigation.create() }); } constructor(props: Props) { this.authService = props.authService; this.navigation = props.navigation; } async execute(params: Params) { await this.authService.login(params); this.navigation.pop(); } }

Slide 38

Slide 38 text

UseCaseʹ઀ଓͨ͠Container export default compose( // useCaseΛѻ͑ΔΑ͏ʹ͢Δ connectUseCase, connect( (state: State): StateProps => ({ submitDisabled: authSelectors.signInForm.isSubmitDisabled(state), values: authSelectors.signInForm.getValues(state) }), null, (stateProps: StateProps, dispatchProps: {dispatch: Dispatch}, ownProps: OwnProps): Props => { const {values, ...p} = stateProps; return { ...p, handleSubmit: () => { // useCase propsʹ౉ͬͯ͘Δؔ਺ͰUseCaseΛCommand(Action)ʹ࣮ͯ͠ߦ ownProps.useCase.commander(LogInUseCase)({...values}); } }; } ) )(SignInView);

Slide 39

Slide 39 text

UseCaseίϚϯυͱίϚϯμʔ export type UseCaseCommand> = { UseCase: Class, id: string, params: any }; export function createCommander>( useCase: Class ): UseCaseCommander { return (params: P): UseCaseCommand => { return { UseCase: useCase, id: createCommandId(), params }; }; }

Slide 40

Slide 40 text

UseCase࣮ߦج൫ΛSagaͰ؂ࢹ export default function *useCaseSaga( executor: UseCaseExecutor ): Generator<*, *, *> { const chan = createUseCaseChannel(executor); yield fork(watchUseCaseEvent, chan); yield takeEvery( useCaseActions.COMMAND_COMMANDED, handleCommand, executor ); }

Slide 41

Slide 41 text

ReducerʹUseCase࣮ߦঢ়ଶΛ ετΞ const actionHandlers = { ..., [USECASE_EVENT_EMITTED]: (state: UseCaseState, action: UseCaseActionUseCaseEventEmitted) => { const {payload: {event}} = action; const useCaseId = getUseCaseId(event.command.UseCase); const processing = [ USECASE_EVENT_TYPES.COMMAND_CANCELED, USECASE_EVENT_TYPES.USECASE_FAILED, USECASE_EVENT_TYPES.USECASE_SUCCEEDED ].indexOf(event.type) < 0; return { ...state, summary: {...state.summary, [useCaseId]: {processing}} }; } }; export default function useCaseReducer(state: UseCaseState = initialState, action: UseCaseAction) { const handler = actionHandlers[action.type]; return handler ? handler(state, action) : state; }

Slide 42

Slide 42 text

υϝΠϯ૚

Slide 43

Slide 43 text

υϝΠϯ૚ͷ࢓૊Έ • ֓೦ຖʹΫϥεΛ࡞Δ • ੍໿ͷ͋ΔϞσϧ͸ͦͷ੍໿Λهड़͢Δ • ϑΥʔϜͷValidationͳͲʹ΋ྲྀ༻͢Δ • ͻͱͭͷϞσϧʹऩ·Βͳ͍΋ͷ͸DomainServiceͱ͢ Δ • RepositoryͳͲ͸FlowͰinterface͚ͩΛఆٛ͢Δ

Slide 44

Slide 44 text

Ϟσϧྫ export default class Username extends ValueObject { static validators = [ maxLength(MAX_LENGTH), format(FORMAT), notNumbersOnly() ]; } export default class Password extends ValueObject { static validators = [ maxLength(MAX_LENGTH), minLength(MIN_LENGTH), format(FORMAT), multipleKindChars({ kinds: [REGEX_NUMBER, REGEX_ALPHABET, REGEX_SYMBOL], min: 2 }) ]; } export default class AuthService { provider: IAuthServiceProvider; accessTokenRepository: IAccessTokenRepository; async login(params: {password: Password, username: Username}): Promise<{accessTokenId: Token}> { const token = await this.provider.login(params); await this.accessTokenRepository.save(token); return {accessTokenId: token.getId()}; } }

Slide 45

Slide 45 text

શମߏ੒

Slide 46

Slide 46 text

Α͍ͱ͜Ζ • Ϣʔβʔى఺ͷॲཧΛ௥͍΍͍͢ • جຊతʹϢʔεέʔεʹ௚ྻʹॻ͔Ε͍ͯΔ • ςετ͠΍͍͢ • UseCase͸DIͬΆ͘ͳͬͯΔͷͰɺmockͯ͠ਖ਼͘͠ݺͼग़͞ Ε͍ͯΔ͔֬ೝ͢Δ͚ͩ • ܕ͕ͪΌΜͱޮ͘ • UseCaseʹඞཁͳύϥϝʔλΛ࣮֬ʹ౉ͤΔ

Slide 47

Slide 47 text

ؾʹͳΔͱ͜Ζ • ͳΜ͔΍Γ͗͢ײ͕͋Δ • ݁ߏ࣮૷͢Δͱ͖ʹߟ͑ͳ͍ͱ͍͚ͳ͍ • Ϟσϧͱ͔֓೦ͱ͔ • ਖ਼௚ࣗ෼Ͱ΋͜ΕͰ͍͍ͷ͔ࣗ৴͕ͳ͍ • ڵຯ͋Δਓμϝग़ͯ͠͠΄͍͠…

Slide 48

Slide 48 text

ͱɺ͍͏ͷ͕ Web൛ͷઃܭͷ࿩

Slide 49

Slide 49 text

εϚϗΞϓϦΛͲ͏͢Δ͔? • Web൛ͱҰ෦Λڞ௨Խ͢Δ • υϝΠϯϞσϧɺϦϙδτϦͷҰ෦ɺAPIΫϥΠΞϯτͳ ͲΛผϦϙδτϦʹ੾Γग़ͯ͠submoduleͰऔΓࠐΉ༧ ఆ • Ϣʔεέʔε΍ίϯϙʔωϯτ͸ڞ௨Խ͠ͳ͍ • UIΛؚΉ΋ͷ͸֤ϓϥοτϑΥʔϜͰͷ࠷దղ͕ҟͳΔ ͸ͣͳͷͰແཧʹྲྀ༻͠ͳ͍

Slide 50

Slide 50 text

·ͱΊ • Ϣʔεέʔε૚υϝΠϯ૚Λͭ͘Δ͜ͱͰʮॲ ཧ͕௥͍ʹ͍͘ʯΛ؇࿨Ͱ͖Δઆ • React NativeͳΒωΠςΟϒͱWebͰɺઃܭ ͚ͩ͡Όͳ͘ɺ࣮૷΋૬ޓʹϑΟʔυόοΫ ͓͋͑ͯ͠ಘ • ਓ(ྗ)͕ཉ͍͠

Slide 51

Slide 51 text

͓ΘΓ

Slide 52

Slide 52 text

ࢀߟࢿྉ • ෳࡶͳJavaScriptΞϓϦέʔγϣϯΛߟ͑ͳ͕Β࡞Δ࿩ • https://azu.github.io/slide/2016/react-meetup/large-scale-javascript.html • Redux΁ͷਖ਼͍͠ղऍͷ࿩ • https://medium.com/@axross/undertanding-truthful-redux-with-usecases-179eefd9fd55 • DDD + Clean Architecture + UCDOM Full൛ • https://speakerdeck.com/yoskhdia/ddd-plus-clean-architecture-plus-ucdom-fullban • όϯυϧΧʔυ͕Ͱ͖Δ·Ͱ • http://ideyuta.com/vandlecard/ • ଞʹ΋ͨ͘͞Μ…