Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ぼくのかんがえた最強のUsecaseの作り方~あるいはビジネスロジックとはなにかという1つの回答~

 ぼくのかんがえた最強のUsecaseの作り方~あるいはビジネスロジックとはなにかという1つの回答~

ビジネスロジック、Domain層、UseCase。これらはMVVMやMVP、CleanArcitectureなど、3-Layeredアーキテクチャーではよく聞く単語だ。

しかし、いざ設計しようとすると
「つまり、ビジネスロジック/Domain/UseCaseって何?」
「レポジトリから取得したデータをそのまま渡すだけになるんだけど、これでいいの?」

などなど、首をひねりながら設計する人は多いのではないだろうか。かくいう私もその一人だった。

今回の発表では、CleanArchitectureにフォーカスし、「ビジネスロジック/Domain/UseCase is 何」という疑問を紐解いていこうと思う。
具体的には個人で開発しているアプリの「らくでん」の実装を元に、
・ビジネスロジックってどういうものを指すのか
・Domain層(UseCase)をどのように設計していくか
・何をDomain層に入れるか/入れないか
を話していく予定だ。

ReferenceURL:
https://github.com/kiuchikeisuke/RakutenCall

Keisuke kiuchi

February 07, 2019
Tweet

More Decks by Keisuke kiuchi

Other Decks in Programming

Transcript

  1. ໦಺ܒี w ৬ۀɿ:ͷ"OESPJEΤϯδχΞ݉ҭࣇύύ w झຯͰ࡞ͬͨϞϊɿʮΒ͘ͰΜ "QQ ʯɺ
 ʮ$MFBO"SDIJUFDUVSF5FNQMBUF'PS,PUMJO 044 ʯ

    w Ϟοτʔɿ໌೔ͷࣗ෼ʹ༏͍͠ίʔυΛॻ͘ w 4/4
 5XJUUFS!GFJ@LPNF
 (JUIVC!LJVDIJLFJTVLF
 2JJUB!L@LFJTVLF
  2. ϏδωενʔϜͷճ౴  1SFpYͷ൪߸͸ʁ
 ˠ๭3͔ࣾΒ࿈བྷ଴ͪͰ͢  6*σβΠϯ͸͍ͭࠒͰ͖Δʁ
 ˠ6*νʔϜʹฉ͍ͯΈͳ͍ͱԿͱ΋  αʔόʔ࿈ܞͱ͔͸͋Δʁͳ͍ʁ
 ˠࠓ࣌఺Ͱ͸Կͱ΋

     ͱ͔ͱ͔ຊདྷి࿩ྉ͕ۚ
 ͔͔Βͳ͍ి࿩൪߸ͷѻ͍͸ʁ
 ˠ1SFpYΛ͚ͭͳ͍ํ਑Ͱ 3͕ࣾ౴͑Λ࣋ͬͯΔ σβΠϯνʔϜ͕౴͑ Λ࣋ͬͯΔ ະདྷͷࢲ͕ͨͪ౴͑Λ ͍࣋ͬͯΔ ϏδωενʔϜ͕౴͑ Λ͍࣋ͬͯͨ ճ૝த
  3. ϏδωεϩδοΫͱ͸ w ϏδωενʔϜ ࢓༷νʔϜ ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ ͷࣄ w ϏδωενʔϜ͚ͩͰܾ·Βͳ͍ࣄ 6*ͷσβΠϯ

    σʔλͷѻ͍ 1SFpYͷ ൪߸ͳͲ ͸ϏδωεϩδοΫʹ͸ؚΊͳ͍ ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍ wઃఆ͕༗ޮͳΒٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍ wઃఆ͕༗ޮͳΒϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍ wແࢹϦετͷѼઌʹ͸1SFpYΛ͚ͭͳ͍ 6*σβΠϯ 1SFpYͷ൪߸ σʔλอଘͷ࢓༷ ٹٸͷి࿩ ൪߸ ϑϦʔμΠΞϧͷ൪߸ Ϣʔβ͕ࢦఆ ͨ͠ແࢹϦετ ઃఆͷ༗ޮͷঢ়ଶ
  4. ઃఆ͕༗ޮͳΒϑϦʔμΠΞϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
 ͷ࣮૷ import io.reactivex.Observable import jp.rakutencall.data.datasource.contacts.ContactsDataSource import jp.rakutencall.data.datasource.settings.SettingDataSource import jp.rakutencall.data.entity.number.PhoneNumber

    import jp.rakutencall.data.entity.prefix.Prefix import jp.rakutencall.utils.commons.ExecutionThreads import jp.rakutencall.utils.commons.IoUseCase import jp.rakutencall.utils.commons.UseCase import javax.inject.Inject class FreeDial @Inject constructor( private val settingDataSource: SettingDataSource, private val contactsDataSource: ContactsDataSource, executionThreads: ExecutionThreads) : IoUseCase<FreeDial.Request, FreeDial.Response, Throwable>(executionThreads) { override fun execute(requestValue: Request): Observable<Response> { return settingDataSource.getFreeDialEnable() .flatMap { if (it) validateFreeDial(requestValue) else Observable.just(Response(requestValue.prefix)) } } private fun validateFreeDial(requestValue: Request): Observable<Response> { return contactsDataSource.getFreeDialPrefix().map { if (it.any { it.startWithPrefix(requestValue.phoneNumber) }) Response(Prefix.generateEmptyPrefix()) } else { Response(requestValue.prefix) } }.defaultIfEmpty(Response(requestValue.prefix)) } data class Request(val prefix: Prefix, val phoneNumber: PhoneNumber) UseCase.RequestValue data class Response(val prefix: Prefix) : UseCase.ResponseValue }
  5. "OESPJEʹ΋42-JUFʹ΋ґଘͯ͠ͳ͍ૄ݁߹ͳΫϥε import io.reactivex.Observable import jp.rakutencall.data.datasource.contacts.ContactsDataSource import jp.rakutencall.data.datasource.settings.SettingDataSource import jp.rakutencall.data.entity.number.PhoneNumber import

    jp.rakutencall.data.entity.prefix.Prefix import jp.rakutencall.utils.commons.ExecutionThreads import jp.rakutencall.utils.commons.IoUseCase import jp.rakutencall.utils.commons.UseCase import javax.inject.Inject
  6. ࢓༷ͦͷ··ͳ࣮૷ ઃఆ͕༗ޮͳΒd class FreeDial @Inject constructor( private val settingDataSource: SettingDataSource,

    private val contactsDataSource: ContactsDataSource, executionThreads: ExecutionThreads) : IoUseCase<FreeDial.Request, FreeDial.Response, Throwable>(executionThreads) { override fun execute(requestValue: Request): Observable<Response> { return settingDataSource.getFreeDialEnable() .flatMap { if (it) validateFreeDial(requestValue) else Observable.just(Response(requestValue.prefix)) } }
  7. ࢓༷ͦͷ··ͳ࣮૷ ϑϦʔμΠΞϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍ private fun validateFreeDial(requestValue: Request): Observable<Response> { return contactsDataSource.getFreeDialPrefix().map

    { if (it.any { it.startWithPrefix(requestValue.phoneNumber) }) { Response(Prefix.generateEmptyPrefix()) } else { Response(requestValue.prefix) } }.defaultIfEmpty(Response(requestValue.prefix)) } data class Request(val prefix: Prefix, val phoneNumber: PhoneNumber) : UseCase.RequestValue data class Response(val prefix: Prefix) : UseCase.ResponseValue }
  8. Ұݴ·ͱΊ w ϏδωεϩδοΫͱ͸ɺ
 ϏδωενʔϜ ࢓༷νʔϜ ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ ͷࣄ w Ϣʔεέʔεͱ͸ɺ


    ϏδωεϩδοΫΛ࢖͏ Ϣʔε έʔε͝ͱʹ෼ׂͨ͠΋ͷ w ͍͖͞ΐʔͳϢʔεέʔεͷ࡞Γํͱ͸ɺ
 ϏδωενʔϜ͚ͩͷձ࿩ͰܾఆͰ͖ͨ࢓༷͕Ϣʔεέʔε w ͍͖͞ΐʔͳϝϦοτͱ͸ɺ
 ΊͬͪΌૄ݁߹ͰՄಡੑͷߴ͍ίʔυ͕Ͱ͖Δ