新規事業立ち上げの際に、実際にどのように開発を進めていっているかについて、お話させせていただきました。要件の洗い出しから、技術選定、アーキテクチャ設計、開発サイクルの回し方、実装方針などについて、少しでも参考になれば嬉しいです。
৽نࣄۀ։ൃΛࢧ͑Δٕज़2018/12/15 Developers BoostSansanגࣜձࣾ DSOC Development Group ΤϯδχΞా༔Ұ
View Slide
ࣗݾհ• ా༔Ұ (mokuo)• Twitter : @mokuo_• 92ੜ·Ε• ݸਓ։ൃͨ͠Γϒϩάॻ͍ͨΓ͠·͢• ϙουΩϟετϥδΦΛΑ͘ௌ͖·͢
ܦྺ• 20182݄ : Sansanגࣜձࣾೖࣾ• Eight ͷϑϩϯτΤϯυɾαʔόʔαΠυͷ։ൃ• React + Redux, Ruby on Rails• 20186݄ : DSOC ʹҟಈ• GEESʢ໊ͷσʔλԽγεςϜʣͷ։ൃɾӡ༻• Ruby on Rails• ৽نࣄۀ։ൃ <= ຊͷ͓ʂʂ
ΞδΣϯμ• ͡Ίʹ• ৽نࣄۀ։ൃΛࢧ͑Δٕज़• ཁ݅ͷચ͍ग़͠• ։ൃϓϩηεͷબఆ• ΞʔΩςΫνϟઃܭ• ٕज़બఆ• DBઃܭ• APIఆٛ• ը໘ઃܭ• ϑϩϯτΤϯυ࣮• αʔόʔαΠυ࣮• ·ͱΊ
͡Ίʹ
ࣄۀ༰ʹ͍ͭͯ• େਓͷࣄʹΑΓɺݱ࣌Ͱɺ৽نࣄۀͷதʹ͍͓ͭͯͰ͖·ͤΜɻ• ࣄۀ༰ʹ৮Εͳ͍ൣғͰ͓͍͖ͤͯͨͩ͞·͢ɻ
ຊͷ͓ʹ͍ͭͯ• ৽نࣄۀ։ൃΛ୲͢Δ͜ͱʹͳͬͯɺ࣮ࡍʹࢲ͕औΓΜͰ͍Δ͜ͱΛ͓͠·͢ɻ• ଞʹ༷ʑͳΓํ͕͋Δͱࢥ͍·͢ɻ͋͘·ͰɺࢀߟใͷҰͭͱͯ͠ฉ͍͍͚ͯͨͩ·͢ͱ͍Ͱ͢ɻ
৽نࣄۀ։ൃΛࢧ͑Δٕज़
લఏ• ։ൃ͢ΔγεςϜෳ• ͦͷதͷج൫తͳγεςϜΛϝΠϯͰ୲
৺͕͚͍ͯΔ͜ͱ
ࣄۀͷ͕ୈҰ• Ұੜݒ໋ͩ͜Θͬͯ࡞ͬͯɺചΕͳ͚ΕɺιϑτΣΞࢮΜͰ͠·͍·͢ɻ• ࠓٻΊΒΕ͍ͯΔͷԿͳͷ͔ɺৗʹߟ͑ΔΑ͏ʹؾΛ͚͍ͭͯ·͢ɻ
ࣄۀͱٕज़ͷόϥϯεΛऔΔ• ͱ͍͑ɺεϐʔυ͚ͩΛ༏ઌ͢ΔͷͰͳ͘ɺٕज़తͳҰઢकΓ͍ͨͱߟ͍͑ͯ·͢• ࣭͠։ൃΛอͭ͜ͱ͕Ͱ͖ΕɺকདྷతʹϖΠͰ͖ΔͷͰͳ͍͔ɺͱࢥͬͨΓ͍ͯ͠·͢ɻ
ཁ݅ఆٛ
δϣΠϯͨ࣌͠ͷঢ়گ• ཁ͕݅·ͩ;Θͬͱ͍ͯ͠Δ͕ɺεέδϡʔϧܾ·͍ͬͯΔ
Ұ൪࠷ॳʹͬͨ͜ͱ• ཁ݅ͷચ͍ग़͠• ݒ೦ͷղফ• ͬ͘͟Γͨ͠ݟੵΓ• εέδϡʔϧௐ
ཁ݅ͷચ͍ग़͠• ϓϩμΫτόοάϩάΛ࡞• ϓϩμΫτΦʔφʔͱҰॹʹɺཁ݅ΛεϓϨουγʔτʹॻ͖ग़ͨ͠
ٙͷղফ• ͕ٙ͋ΔͱɺݟੵΓ͕Ͱ͖ͳ͍• ୭ʹ֬ೝ͢Δ͔Ͱྨ• ࣗͰௐΔ, PO, νʔϜA, νʔϜB, …• ҰͭҰͭ௵͍ͯͬͨ͠
ͬ͘͟Γͨ͠ݟੵΓ• தؒ͘Β͍ͷқͷλεΫΛ 3 ϙΠϯτͱ͢Δ• 1, 3, 5, 8, 13 ͱϙΠϯτΛ͚ͯ૬ରݟੵΓΛ࣮ࢪ• Πϝʔδ͍͢͠λεΫΛબͼɺԿ͔͔࣌ؒΓͦ͏͔ݟੵͬͯΈΔ• શମͰ͔͔ΔΛܭࢉ
εέδϡʔϧௐ• ཁ݅Λ͗མͱͯ͠εέδϡʔϧʹؒʹ߹ΘͤΔ͔ɺεέδϡʔϧΛ৳͔͢ަব• डୗ։ൃͳΒɺ2, 3ഒͷόοϑΝΛੵΈ͍ͨͱ͜Ζ͕ͩɺࣗࣾαʔϏεͰ͋Εɺ͋ΔఔॊೈʹௐͰ͖Δ
։ൃϓϩηεͷબఆ
બࢶ1 : ΥʔλʔϑΥʔϧ• ઌʹεέδϡʔϧ͕ܾ·͍ͬͯΔͷͰ͋Εɺ࠷ॳΥʔλʔϑΥʔϧͰྑ͍͔͠Εͳ͍• σϝϦοτ : ཁٻ͕มΘͬͨͱ͖ʹɺॊೈʹରԠͮ͠Β͍
બࢶ2 : ΞδϟΠϧ• ΞδϟΠϧιϑτΣΞͷ12ͷݪଇ• Ձͷ͋ΔιϑτΣΞΛૣ͘ܧଓతʹఏڙ͠·͢ɻ• ཁٻͷมߋͨͱ͑։ൃͷޙظͰܴ͋ͬͯ͠·͢ɻ=> ৽نࣄۀʹద͍ͯ͠Δͱஅ
εΫϥϜΛ࠾༻• վΊͯɺ͍͔ͭ͘ͷจݙΛಡΈͨ͠• ΞδϟΠϧιϑτΣΞ։ൃએݴ• εΫϥϜΨΠυ• ࠓ·Ͱͷܦݧ౿·͑ͯɺ۩ମతͳΠϕϯτఆٛ͞Ε͍ͯΔεΫϥϜ͕ྑ͍ͱஅ
ܗࣜʹͩ͜ΘΓա͗ͳ͍• ઐ༻ޠΛΘͳ͍• ϓϩμΫτόοάϩά => λεΫ• εϓϦϯτϓϥϯχϯά => ܭը• ϨτϩεϖΫςΟϒ => ;Γ͔͑Γ• ϓϩμΫτόοΫϩάεϓϨουγʔτͰཧ• ϓϩμΫτΦʔφʔ͕͍͍͢ͷΛ࠾༻• υϥοά&υϩοϓͰॱ൪ΛೖΕସ͑ΒΕΔ͠ɺࠓͷͱ͜Ζࠔ͍ͬͯͳ͍
ΞʔΩςΫνϟઃܭ
લఏ• ୲͢ΔγεςϜ Web ΞϓϦέʔγϣϯͷΈͰɺϞόΠϧΞϓϦ։ൃ͠·ͤΜɻ• ӡ༻ෛՙΛԼ͍͛ͨͱ͍͏ࢥ͍͕͋Γ·͢ɻ
ΞʔΩςΫνϟઃܭͷมભᶃ SPA + Firebase⊗ᶄ ϚΠΫϩαʔϏεΞʔΩςΫνϟ on GAE⊗ᶅ ࠷ॳαʔϏεׂ࠷খݶʹ͠Α͏ <= ݱࡏ
SPA + Firebase
SPA + Firebase• JavaScript ͷॲཧඞཁ• ϑϩϯτΤϯυͰҰఆҎ্ͷॲཧΛ͢ΔͳΒ SPA ʹͯ͠͠·ͬͨํ͕ྑ͍ʢͱࢥ͍ͬͯΔʣ• ͔͠͠ɺϑϩϯτΤϯυͱαʔόʔαΠυͷ྆ํΛ࣮͢Δ͜ͱ͕͔͔Δ• Cloud Firestore ͳΒϑϩϯτΤϯυ͔Βॻ͖ࠐΊΔ• ଟ͘ͷ API ࣮͢Δඞཁ͕ͳ͘ͳΔ…ͣɻ
۩ମతʹ• Firebase Authentication Ͱೝূ• Cloud Firestore, Cloud Storage ͷॻ͖ࠐΈΛݕͯ͠ɺCloud Functions ΛΒͤΔ͜ͱ͕Ͱ͖Δ• όοΫΤϯυͷॲཧτϦΨʔத৺ʹ͢Δ
SPA + FirebaseτϦΨʔ
ݒ೦• ϐλΰϥεΠονతͳγεςϜʹͳΔ͜ͱ͕ఆ͞ΕΔ• কདྷతʹॲཧ͕ෳࡶʹͳͬͯདྷͨͱ͖ʹɺCloudFunctions ͚ͩͰΓΕΔͷ͔• Firebase Authentication B2C ͖ʹݟ͑Δ• ࡉ͔͍ݖݶཧΛ͢ΔͨΊʹɺΈΛߟ͑ͯࣗલͰ࣮͢Δඞཁ͕͋Δ
ͦͦΓ͔ͨͬͨ͜ͱ• ӡ༻ෛՙΛԼ͍͛ͨ• Firebase ʹͩ͜ΘΔඞཁͳ͍
ϚΠΫϩαʔϏεΞʔΩςΫνϟon Google App Engine
Google App Engine• GCP ͕ఏڙ͢Δ PaaS• ϦΫΤετʹԠͯࣗ͡ಈͰεέʔϧ• ίϚϯυҰൃͰσϓϩΠͨ͠Βಈ͘• Function ୯ҐͰͳ͘ɺαʔϏε୯ҐͰσϓϩΠ• FaaS ʹൺͯɺෳࡶͳॲཧʹରԠ͍ͣ͢͠
Microservices Architecture onGoogle App Engine• DB, Ωϡʔ ͳͲڞ༗
جຊͷߟ͑ํ• αʔϏεؒͷΓͱΓجຊ Cloud Tasks Λܦ༝• DB Ҿ͖ଓ͖ Cloud Firestore Λ࠾༻• ϑϩϯτΤϯυ͔Β DB Λ৮Δ͜ͱͤͣɺAPIܦ༝ͰΞΫηεͤ͞Δ• কདྷతʹɺ৮Δ͜ͱ͋Δ͔͠Εͳ͍ʢཧը໘ͱ͔ʁʣ• ෳαʔϏεͰίϨΫγϣϯڞ༗͠ͳ͍
σʔλϕʔεԿʹ͢Δ͔• Cloud Datastore• ࣗಈεέʔϦϯάɾύϑΥʔϚϯεΛߟ͑ΔͱɺGAE ʹద͍ͯ͠Δ• Cloud SQL for MySQL• Cloud SQL for PostgreSQL
Cloud Firestore ΛબެࣜυΩϡϝϯτ ↓↓• Cloud Firestore ͱ Cloud Datastore ͷબ> Cloud Firestore ɺCloud Datastore ͷϦϒϥϯσΟϯάͱͳΔ࣍ظϝδϟʔ όʔδϣϯͰ͢• Cloud Firestore ͷࣗಈΞοϓάϨʔυ• Cloud Datastore Cloud Firestore ʹΞοϓάϨʔυ͞ΕΔ༧ఆ
Cloud Firestore ͷϞʔυ• Cloud Firestore ͱ Cloud Datastore ͷબ• Cloud Firestore in Datastore mode• Cloud Firestore in Native mode• ͦΕͧΕͷಛੑΛݟͯஅ
ϚΠΫϩαʔϏεΞʔΩςΫνϟ• ຊͰମܥతͳࣝΛೖΕ͢• ಡΊಡΉ΄ͲɺGAE ϚΠΫϩαʔϏεͷͨΊͷPaaS ͩͱࢥ͑ͯ͘Δ
αʔϏεؒͷ࿈ܞύλʔϯ
༻͢Δओͳ GCP ͷαʔϏε• App Engine• Cloud Storage• Cloud Pub/Sub• Cloud Tasks• Cloud Firestore
Cloud Pub/Sub Notificationsfor Cloud StorageCloud Pub/SubมߋΛݕ Service A
Cloud Pub/SubService CService BService A Cloud Pub/Sub
Cloud TasksService A Service B
ϑϩϯτΤϯυ͔Β API Λୟ͘Frontend Service Backend Service
࠷ॳαʔϏεׂ࠷খݶʹ͢Δ
࣌ظঘૣͳղ• ༷ʑͳใɺࣾ֎ͷΤϯδχΞͱͷରΛ௨ͯ͠ɺ࠷ॳ͔ΒαʔϏεΛׂ͢Δͷةݥͱஅ• ·ͣ࠷খݶͰελʔτ͢Δ༧ఆͰ͢
ඇಉظॲཧ• Cloud Tasks ΤϯυϙΠϯτΛࢦఆͯ͠Ωϡʔʹ٧ΊΔService Aࣗࣗʹͬͯ͘Δ
ٕज़બఆ
ϑϩϯτΤϯυ
ϑϩϯτΤϯυ• React + Redux• TypeScript• Next.js
React + Redux• Eight Ͱ React Λ͍ͬͯΔ͜ͱ͋Γɺ׳Ε͍ͯΔɻݸਓతʹ Vue ΑΓ͖• React ͳΒ Redux, Vue ͳΒ Vuex ͳͲɺFlux ΞʔΩςΫνϟΛ͏͜ͱɺ΄΅ඞਢͩͱࢥ͍ͬͯΔ• ΞϓϦέʔγϣϯશମͰ State Λڞ༗Ͱ͖Δ• ڊେͳ JSON Λ͍࣋ͬͯΔΠϝʔδ
TypeScript• ࣄݸਓ։ൃͰ JS Λॻ͍͍ͯͯɺܕ͕ͳ͍ਏ͞Λײ͍ͯͨ͡• Promise ͕ೖͬͯ͘ΔͱɺԿ͕ฦ͖͍ͬͯͯΔͷ͔Θ͔Βͳ͘ͳͬͨΓ͢Δ• ࣮ߦ࣌ΤϥʔͰ͢ɺΛ܁Γฦ͢• TypeScirpt ΛೖΕͯɺIDE తͳԸܙΛड͚ΒΕΔ• ͪΖΜɺٯʹେมͳ͜ͱ͋Δ
Next.js• pages/ ҎԼʹίϯϙʔωϯτஔ͘ͱࣗಈͰϧʔςΟϯάͯ͘͠ΕΔ• αʔόʔαΠυϨϯμϦϯά؆୯• ಋೖ͓͍ͯͯ͠ѱ͍͜ͱͳ͍ͱஅ
αʔόʔαΠυ
αʔόʔαΠυ• Node.js• Express• TypeScript• swagger-node
αʔόʔαΠυͷݴޠΛԿʹ͢Δ͔• ݸਓతʹɺ෦ॺͱͯ͠ɺಘҙͳͷ Rails• ͔͠͠ɺݱ࣌ͷ Google App Engine ͷStandard Environment Ͱ Ruby ͕͑ͳ͍
GAE ͷ2छྨͷڥʹ͍ͭͯ• Standard Environment• ݱ࣌Ͱ Python, Java, PHP, Go, Node.js ͷΈ• Flexible Environment• ্هʹՃ͑ͯ .NET, Ruby, ΧελϜϥϯλΠϜ͕͑Δ
֤ڥͷൺֱʢൈਮʣ• https://cloud.google.com/appengine/docs/the-appengine-environmentsػೳ4UBOEBSE&OWJSPONFOU'MFYJCMF&OWJSPONFOUΠϯελϯεىಈ࣌ؒ ϛϦඵ ϦΫΤετͷ࠷େλΠϜΞτඵ εέʔϦϯά खಈɺجຊɺࣗಈ खಈɺࣗಈΧελϚΠζՄೳͳॲཧελοΫº˓ʢ%PDLFSpMFͷΧελϚΠζͰߏஙʣྉۚΠϯελϯε࣌ؒʹجͮ͘W$16ɺϝϞϦɺӬଓσΟεΫͷ༻ྔʹجͮ͘
͖͢ҧ͍• Πϯελϯεىಈ࣌ؒ• ྉۚ
جຊ Standard Environment• ඞཁͳ͚࣌ͩ Flexible Environment ʹ͢Δ• ࠓճը૾ॲཧʹඞཁͳϥΠϒϥϦΛ͏ͨΊʹɺGAE/FE Ͱ Docker Λ༻
Node.js Λ࠾༻• ݸਓతʹɺRuby ͷ࣍ʹಘҙͳݴޠ JS• Node.js AWS Lambda, Google CloudFunctions Ͱͬͨ͜ͱ͕͋Δ• ࠓޙδϣΠϯ͢ΔϝϯόʔʹɺϑϩϯτΤϯυαʔόʔαΠυݟͯ΄͍͠ͱ͍͏ࢥ͍͋Δ
ϑϨʔϜϫʔΫ Express• Node.js ͰҰ൪ΘΕ͍ͯΔϑϨʔϜϫʔΫɺExpress ͩͱࢥΘΕΔ• ܰྔͳϑϨʔϜϫʔΫͰɺϚΠΫϩαʔϏεʹྑͦ͞͏
TypeScript• ϑϩϯτΤϯυͱಉ༷ʹ TypeScript Λ༻
swagger-node• Swagger Ͱ API ఆٛΛॻ͘ͱɺϧʔςΟϯάϦΫΤετͷόϦσʔγϣϯΛͯ͘͠ΕΔ• ࣮ଶ swagger-express-mw• ϝϯς͕ߦ͖ಧ͍͍ͯͳ͍ײ͢͡ΔͷͰɺཁҙ• swagger editor ೖ͍ͬͯΔ͕ɺόʔδϣϯ͕ݹ͍• swagger project create ίϚϯυࢀߟఔʹ༻͢Δ
CI• طʹࣾͰར༻͍ͯͯ͠ɺಛʹ՝ײͳ͍ͷͰɺCircleCI, Sider Λ༻• CircleCI Ͱ Jest• Sider Ͱ TSLint• CD Γ͍ͨ
DBઃܭ
Cloud Firestore• NoSQL υΩϡϝϯτࢦσʔλϕʔε• جຊతʹɺRDB ͷΑ͏ͳਖ਼نԽ͠ͳ͍• ؔ࿈αϒίϨΫγϣϯͱ͍͏ܗͰωετ͢Δ
εϓϨουγʔτͰઃܭ• ͬͱྑ͍Γํ͕͋Εڭ͍͑ͯͩ͘͞…• ↓ Πϝʔδ
APIઃܭ
RESTful API• Rails ͷϧʔςΟϯάΛࢀߟ• Rails ͷϧʔςΟϯάڧྗ• `resouces: photos` ͷΑ͏ʹॻ͚ͩ͘ͰɺϦιʔεϕʔεͷنଇతͳϧʔςΟϯά͕Ͱ͖Δ• Rails ͷϧʔςΟϯά | Rails ΨΠυ ࢀর
Swagger Λ༻• APIυΩϡϝϯτͷσϑΝΫτελϯμʔυʹͳ͖͍ͬͯͯΔͱࢥΘΕΔ• swagger-express-mw ͰϧʔςΟϯάͱϦΫΤετͷόϦσʔγϣϯ͕Ͱ͖Δ͜ͱେ͖͍• ͍ͣΕʹͤΑɺAPIυΩϡϝϯτඞཁ
GraphQL ʁ• ϑϩϯτ <=> αʔόʔؒ GraphQL Ͱྑͦ͞͏• طʹɺࣗͱͯ͠෦ॺͱͯ͋͠·Γͬͯ͜ͳ͔ٕͬͨज़ΛΓࠐΜͰ͍Δ• GCP, TypeScript, Node.js, SPA, Atomic Design…• ࠓճݟૹΓ
ίϯϙʔωϯτઃܭ
Atomic Design• σβΠϯʹΦϒδΣΫτࢦతͳߟ͑ํΛऔΓೖΕͨײ͡• UI ͷมߋʹڧ͘ɺίϯϙʔωϯτΛ࠶ར༻͍͢͠
ϑϩϯτΤϯυ࣮
ίϯϙʔωϯτ
Storybook• Storybook Λ͏͜ͱͰɺίϯϙʔωϯτ͝ͱʹಈ࡞֬ೝ͠ͳ͕Β։ൃͰ͖Δ• Storyshot ͰεφοϓγϣοτΛऔΔ͜ͱͰ͖Δ• Storyshot ʹؔͯ͠ɺTypeScript, Next.js ༻͍ͯ͠Δ͔Β͔ɺΤϥʔ͕ग़ͯղফͰ͖ͣ…• ଞͷํ๏Λݕ౼த
Redux
Actions• Flux Standard Action (FSA) ʹै͏͜ͱ͕ਪ͞Ε͍ͯΔ• FSA ʹԊͬͨϥΠϒϥϦ͍͔ͭ͋͘Δ• redux-actions Λ͓͏ͱ͕ͨ͠ɺTypeScript ͱ૬ੑ͕ѱ͔ͬͨʢܕఆ͕ٛෆશʣ• typescript-fsa Λ࠾༻
Reducers• Immutable.js Λ༻• State ͕ωετͨ͠Γෳࡶʹͳͬͯ͘ΔͱɺReducer ͕େมͳ͜ͱʹͳΔ• Object.assign() ਏ͍• Immutable.js ͷ Record Λ͏͜ͱͰɺϞσϧʹϝιουΛੜͯ͠ Reducer ΛγϯϓϧʹͰ͖Δ
ࣗಈςετ• Jest Λ࠾༻• RSpec ϥΠΫͳͷͰɺRuby ग़ऀೃછΈ͍ͣ͢• ΦʔϧΠϯϫϯͳͷྑ͍• Actions ͱ Reducers ͷςετඞͣॻ͘• ίϯϙʔωϯτɺϩδοΫΛॻ͍ͨϝιουςετ͢Δ• ίϯϙʔωϯτࣗମͷςετࡧத
αʔόʔαΠυ࣮
Express ͷ͓࡞๏͕Θ͔Βͳ͍
ௐͯݟ͖͑ͯͨಛ• ExpressɺϧʔςΟϯάͱϛυϧΣΞ͕جຊతͳ֓೦Β͍͠
جຊํ• Swagger Ͱ API ఆٛ• ίϯτϩʔϥʔʹϝιουΛ࣮• swagger-node ͰϧʔςΟϯάɺϦΫΤετͷόϦσʔγϣϯΛͯ͘͠ΕΔ• DB ·ΘΓͷॲཧϞσϧͰߦ͏• ෳࡶͳॲཧϞδϡʔϧʹΓग़͢• ೝূɺΤϥʔϋϯυϦϯάͳͲ middleware Ͱߦ͏
ࣗಈςετ• ϑϩϯτΤϯυͱಉ͘͡ Jest• Jest σϑΥϧτͰฒߦͰςετ͕ΔͷͰૣ͍͕…• CI ༻ͷϓϩδΣΫτΛ࡞͍ͬͯΔ͕ɺFirestore Ұͭ• ςετಉ͕࢜ׯবͯ͠ɺͨ·ʹམͪΔ => ྑ͘ͳ͍• ରԠࡦݕ౼த• কདྷతʹ E2E ςετ͋ΔఔࣗಈԽ͍ͨ͠
·ͱΊ
·ͱΊ• ͦͷࣄۀʹ͓͍ͯɺԿ͕ٻΊΒΕ͍ͯΔ͔ߟ͑Δ• ٕज़తʹ͍͚͑ͯͳ͍ҰઢकΔ• ΑΓྑ͍બΛ͢ΔͨΊʹɺࠒͷ࿉Λ͔ܽ͞ͳ͍
ΤϯδχΞืूʂʂ• Sansan ͰɺੈքΛม͑Δʮग़ձ͍ʯΛੜΈग़͢ΤϯδχΞΛืू͍ͯ͠·͢ɻ• Twitter ͷ DM ͳͲͰߏ͍·ͤΜͷͰɺؾܰʹ͝࿈བྷ͍ͩ͘͞ɻΧδϡΞϧʹ͓͠·͠ΐ͏ʂʂ