Slide 1

Slide 1 text

Backend for mobile app Lesson learned from LINE MAN app Somkiat Khitwongwattana Staff Software Engineer at LINE MAN Wongnai

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

And more

Slide 4

Slide 4 text

And more

Slide 5

Slide 5 text

Client Server

Slide 6

Slide 6 text

Client UI-related data? Server

Slide 7

Slide 7 text

Client UI-related data? App version? Server

Slide 8

Slide 8 text

Client UI-related data? App version? Platform? Server

Slide 9

Slide 9 text

Client UI-related data? App version? Platform? Server Backend Developer

Slide 10

Slide 10 text

Client Server

Slide 11

Slide 11 text

Client Backend for Frontend General Purpose Server

Slide 12

Slide 12 text

Thank you

Slide 13

Slide 13 text

Thank you

Slide 14

Slide 14 text

Frontend Backend for Frontend General Purpose Backend

Slide 15

Slide 15 text

Mobile Backend for Mobile Web Backend for Web General Purpose Backend

Slide 16

Slide 16 text

Simplify Aggregate Modify Benefit Cache

Slide 17

Slide 17 text

Fast Deliver Error Handle Configure Benefit Version Control

Slide 18

Slide 18 text

Server-driven UI SDUI Benefit

Slide 19

Slide 19 text

Data General purpose Style UI Attribute Layout How UI will be Type of SDUI data

Slide 20

Slide 20 text

Backend for mobile app in LINE MAN And more

Slide 21

Slide 21 text

2019 Beginning of BFF in LINE MAN app

Slide 22

Slide 22 text

Data Style

Slide 23

Slide 23 text

{ "data": { "orderId": "LMF-220929-275258587", "userId": "U282752035040160", "orderStatus": "ASSIGNING_DRIVER", "actualStatus": "ASSIGNING_DRIVER", "orderType": "FOOD", "restaurantName": "อิซาโอะ (Isao) Isao", ... }, "root": { "child": [ ... ], "type": "FRAME" } }

Slide 24

Slide 24 text

{ "data": { "orderId": "LMF-220929-275258587", "userId": "U282752035040160", "orderStatus": "ASSIGNING_DRIVER", "actualStatus": "ASSIGNING_DRIVER", "orderType": "FOOD", "restaurantName": "อิซาโอะ (Isao) Isao", ... }, "root": { "child": [ ... ], "type": "FRAME" } }

Slide 25

Slide 25 text

{ "data": { "orderId": "LMF-220929-275258587", "userId": "U282752035040160", "orderStatus": "ASSIGNING_DRIVER", "actualStatus": "ASSIGNING_DRIVER", "orderType": "FOOD", "restaurantName": "อิซาโอะ (Isao) Isao", ... }, "root": { "child": [ ... ], "type": "FRAME" } }

Slide 26

Slide 26 text

"data": { ... }, "root": { "child": [ { "child": [ { "text": "Food Delivery", "id": "topbar_title", "type": "TEXT" }, { "text": "Cancel", "id": "topbar_action_button", "type": "BUTTON", "action": "CANCEL_ORDER" } ], "id": "topbar", "type": "CONTAINER" },

Slide 27

Slide 27 text

"data": { ... }, "root": { "child": [ { "child": [ { "text": "Food Delivery", "id": "topbar_title", "type": "TEXT" }, { "text": "Cancel", "id": "topbar_action_button", "type": "BUTTON", "action": "CANCEL_ORDER" } ], "id": "topbar", "type": "CONTAINER" },

Slide 28

Slide 28 text

"data": { ... }, "root": { "child": [ { "child": [ { "text": "Food Delivery", "id": "topbar_title", "type": "TEXT" }, { "text": "Cancel", "id": "topbar_action_button", "type": "BUTTON", "action": "CANCEL_ORDER" } ], "id": "topbar", "type": "CONTAINER" },

Slide 29

Slide 29 text

"data": { ... }, "root": { "child": [ { "child": [ { "text": "Food Delivery", "id": "topbar_title", "type": "TEXT" }, { "text": "Cancel", "id": "topbar_action_button", "type": "BUTTON", "action": "CANCEL_ORDER" } ], "id": "topbar", "type": "CONTAINER" },

Slide 30

Slide 30 text

"data": { ... }, "root": { "child": [ ..., { "child": [ { "text": "Please wait a moment", "attributes": { "color": "#ffffff", "fontSize": 18, "fontStyle": "SEMI_BOLD", "alignment": "CENTER" }, "id": "overlay_title", "type": "TEXT" }, { "text": "We are searching for ...", "attributes": { "color": "#ffffff", "fontSize": 12,

Slide 31

Slide 31 text

Backend for Mobile General Purpose Backend

Slide 32

Slide 32 text

UI Component TextComponent TextConcatComponent ButtonComponent ImageComponent ContainerComponent ListComponent LazyLoadComponent

Slide 33

Slide 33 text

UI Component TextComponent TextConcatComponent ButtonComponent ImageComponent ContainerComponent ListComponent LazyLoadComponent

Slide 34

Slide 34 text

UI Component TextComponent TextConcatComponent ButtonComponent ImageComponent ContainerComponent ListComponent LazyLoadComponent

Slide 35

Slide 35 text

UI Component TextComponent TextConcatComponent ButtonComponent ImageComponent ContainerComponent ListComponent LazyLoadComponent

Slide 36

Slide 36 text

"root": { "child": [ ..., { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000001", "type": "LAZY_LOAD", "payload": { "type": "ICON_COLLECTION", "link": "/path/to/api/campaign-1234", "title": "Food Icon", "subtitle": { ... } }, "tracking": { ... } }, { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000002", "type": "LAZY_LOAD", "payload": { ... },

Slide 37

Slide 37 text

"root": { "child": [ ..., { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000001", "type": "LAZY_LOAD", "payload": { "type": "ICON_COLLECTION", "link": "/path/to/api/campaign-1234", "title": "Food Icon", "subtitle": { ... } }, "tracking": { ... } }, { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000002", "type": "LAZY_LOAD", "payload": { ... },

Slide 38

Slide 38 text

"root": { "child": [ ..., { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000001", "type": "LAZY_LOAD", "payload": { "type": "ICON_COLLECTION", "link": "/path/to/api/campaign-1234", "title": "Food Icon", "subtitle": { ... } }, "tracking": { ... } }, { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000002", "type": "LAZY_LOAD", "payload": { ... },

Slide 39

Slide 39 text

"root": { "child": [ ..., { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000001", "type": "LAZY_LOAD", "payload": { "type": "ICON_COLLECTION", "link": "/path/to/api/campaign-1234", "title": "Food Icon", "subtitle": { ... } }, "tracking": { ... } }, { "path": "/path/to/api/lazy-item", "params": { ... }, "id": "00000002", "type": "LAZY_LOAD", "payload": { ... }, Static UI Static UI

Slide 40

Slide 40 text

Mobile Backend for Mobile General Purpose Backend

Slide 41

Slide 41 text

Mobile Backend for Mobile General Purpose Backend REST REST REST REST REST REST REST REST REST REST REST REST REST

Slide 42

Slide 42 text

Mobile Backend for Mobile General Purpose Backend gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC REST REST REST REST

Slide 43

Slide 43 text

Mobile Backend for Mobile General Purpose Backend gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC REST REST REST REST

Slide 44

Slide 44 text

Mobile Backend for Mobile General Purpose Backend gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC REST

Slide 45

Slide 45 text

Mobile Backend for Mobile General Purpose Backend gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC REST

Slide 46

Slide 46 text

Mobile Backend for Mobile General Purpose Backend gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC gRPC REST

Slide 47

Slide 47 text

Issue Analyze Payload Size Code Complexity Trade-off Team Effort

Slide 48

Slide 48 text

Knowledge Transfer Not Fully Dynamic UI Trade-off

Slide 49

Slide 49 text

Good for some use case, not all Think twice before implementing

Slide 50

Slide 50 text

More simple

Slide 51

Slide 51 text

Data

Slide 52

Slide 52 text

{ "addressBook": [ ... ], "recentAddresses": [ ... ], "doubleBanner": { "coupon": { "title": "No coupon yet", "subtitle": "Let's find some!" }, "packages": { "title": "Get packages", "subtitle": "Buy & save more", "deepLink": "..." } }, "imageBanner": { "headers": [ ... ], "footers": [ ... ] } }

Slide 53

Slide 53 text

{ "addressBook": [ ... ], "recentAddresses": [ ... ], "doubleBanner": { "coupon": { "title": "No coupon yet", "subtitle": "Let's find some!" }, "packages": { "title": "Get packages", "subtitle": "Buy & save more", "deepLink": "..." } }, "imageBanner": { "headers": [ ... ], "footers": [ ... ] } }

Slide 54

Slide 54 text

{ "addressBook": [ ... ], "recentAddresses": [ ... ], "doubleBanner": { "coupon": { "title": "No coupon yet", "subtitle": "Let's find some!" }, "packages": { "title": "Get packages", "subtitle": "Buy & save more", "deepLink": "..." } }, "imageBanner": { "headers": [ ... ], "footers": [ ... ] } }

Slide 55

Slide 55 text

Partial force update

Slide 56

Slide 56 text

Screen with BFF Screen without BFF Home Restaurant Payment

Slide 57

Slide 57 text

App version 4.3.0 Screen with BFF Screen without BFF Home Restaurant Payment /api/payment/v1/payments

Slide 58

Slide 58 text

App version 7.5.0 Screen with BFF Screen without BFF Home Restaurant Payment /api/payment/v2/payments

Slide 59

Slide 59 text

/api/payment/v2/payments Data response /api/payment/v1/payments Update required error response

Slide 60

Slide 60 text

What’s next ● Dynamic UI ● More adoption ● App configuration ● Feature flag

Slide 61

Slide 61 text

Things to think about Before taking action

Slide 62

Slide 62 text

Tech Stack Data Format Maintainer Before taking action Issue Analyze

Slide 63

Slide 63 text

Knowledge Transfer Necessary ? User Action Before taking action Backward Compatible

Slide 64

Slide 64 text

Deployment Release Cycle Monitoring Before taking action Incident

Slide 65

Slide 65 text

Summary ● Recommended for middle-to-large-scale product ● Not silver bullet ● Team discussion is required ● No one solution for all

Slide 66

Slide 66 text

Backend for frontend (BFF) pattern— why do you need to know it? medium.com/mobilepeople/backend-for-frontend-pattern-why-you-need-to-know-it-46f94ce420b0 Why “Backend For Frontend” Application Architecture? mobilelive.ca/blog/why-backend-for-frontend-application-architecture The BFF Pattern (Backend for Frontend): An Introduction blog.bitsrc.io/bff-pattern-backend-for-frontend-an-introduction-e4fa965128bf Pattern: Backends For Frontends samnewman.io/patterns/architectural/bff/ A Deep Dive into Airbnb’s Server-Driven UI System medium.com/airbnb-engineering/a-deep-dive-into-airbnbs-server-driven-ui-system-842244c5f5 Getting started resources

Slide 67

Slide 67 text

SDUI Framework DivKit github.com/divkit/divkit Beagle github.com/ZupIT/beagle

Slide 68

Slide 68 text

Thank you (Yes, it’s really over) Somkiat Khitwongwattana @akexorcist