a micro service story.
What’s the perfect website for a coder community?
- everybody can contribute
- home page: Welcome! Who are we?
- event pages: when do we meet / announcements
- services: TechRadar, forum, messaging
- apis: extension points for other communities
- content for coders!
What’s the perfect website for a coder community?
hub.coding.earth
When I post an URL
coding.earth shall
determine if it can handle it
extract content
(find related content)
(tag the content (automatically?) )
index the content
display the new content.
When I search for “ruby” on coding.earth
It shall
ask the index for “ruby” content
(apply location / personal / date context)
adjust the page accordingly
The naīve approach
A synchronous http web service
https://meetup.com/colei/events/123+ Add
persistence
Meetups
elasticsearch
POST url
{
url: https://meetup.com/colei/events/123
}
services
The naīve way
Service “gateway”
chooses the right service to talk to
TweetsArticles
https://meetup.com/colei/eve+ GO
persistence elasticsearch
services
The naīve way
Service “gateway”
chooses the right service to talk to
https://meetup.com/colei/events/123
Meetups TweetsArticles
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/colei
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
https://meetup.com/colei/eve+ GO
persistence elasticsearch
services
The naīve way
Service “gateway”
chooses the right service to talk to
https://meetup.com/colei/events/123
Meetups TweetsArticles
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/colei
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
any url here+ GO
persistence elasticsearch
New meetup: “Summer hack”
services
The naīve way
Service “gateway”
chooses the right service to talk to
Your url has been processed
Meetups
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/colei
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
any url here+ GO
persistence elasticsearch
POST url
{
url: https://meetup.com/colei/events/123
}
New meetup: “Summer hack”
services
The naīve way
Service “gateway”
chooses the right service to talk to
Your url has been processed
Meetups
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/colei
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
any url here+ GO
persistence elasticsearch
POST url
{
url: https://meetup.com/colei/events/123
}
New meetup: “Summer hack”
services
Service “gateway”
chooses the right service to talk to
Your url has been processed
Meetups
The naīve approach
A synchronous http web service
- requests keep synchonously running
- any failure in any service can break the request
- strong coupling between “request” and service
layer
- the gateway must choose an interface
understood by all services
- all services are chain-requested
→  hard to scale
→  what if more than 1 service can respond?
The micro service way
event driven services
1. adding data
Coordinator
server side discovery
persistence
Event stream
elasticsearch
client event stream
services
https://meetup.com/colei/events/123+ GO
Coordinator
any url here+ GO
persistence
Event stream
elasticsearch
NewUrl
https://meetup.com/colei/events/123
client event stream
services
+client request id
Http event→ 
POST url
{
url: https://meetup.com/colei/events/123
}
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/217
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
Coordinator
https://meetup.com/colei/events/123+ GO
persistence
Event stream
Meetup.com
elasticsearch
services
NewUrl
https://meetup.com/colei/events/123
client event stream
POST url
{
url: https://meetup.com/colei/events/123
}
Event
service→ 
index→ 
mediumtwitter youtube
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/217
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
Coordinator
persistence
Event stream
Meetup.com
NewContent
/meetup/123
Content: {...}
interface: date
elasticsearch
services
idx
service→ 
event→ 
https://meetup.com/colei/events/123+ GO
Coordinator
persistence
Event stream
NewContent
/meetup/123
Content: {...}
interface: date
elasticsearch
New meetup: “Summer hack”
client event stream
services
+client request id
url has been processed
client request id
→  event
frontend→ 
https://meetup.com/colei/events/123+ GO
/date/314
{
ref: /meetup/123,
starts: “Aug, 3rd”
}
persistence
Event stream
NewContent
/meetup/123
Content: {...}
interface: date
calendar
elasticsearch
NewContent
/date/312
interface: date
services
event
service→ 
index→ 
https://meetup.com/colei/events/123+ GO
Coordinator
Coordinator
persistence
Event stream
elasticsearch
client event stream
services
New calendar entry
“Summer hack”
→  event
frontend→ 
https://meetup.com/colei/events/123+ GO
NewContent
/date/312
interface: date
/date/314
{
ref: /meetup/123,
starts: “Aug, 3rd”
}
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/217
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
Coordinator
eventbrite
persistence
Event stream
Meetup.comtwitter
NewContent
/meetup/123
Content: {...}
interface: date
calendar
elasticsearch
NewContent
/date/312
interface: date
client event stream
New meetup: “Summer hack”
services
New calendar entry
“Summer hack”
+client request id
Your url has been processed
client request id
NewUrl
https://meetup.com/colei/events/123
POST url
{
url: https://meetup.com/colei/events/123
}
https://meetup.com/colei/events/123+ GO
The micro service way
event driven services
2. getting data
services
persistence
frontend app
GET /
200 OK
{
“meetups.”: {
endpoint: ‘meetups.’,
<metadata>
},
“calendar.”: {},
...
}
micro frontends
Coordinator Service
discovery
services
persistence
<Meetup Groups> <Calendar>
frontend app
200 OK
{
“meetups.”: {
endpoint: ‘meetups.’,
<metadata>
},
“calendar.”: {},
...
}
<meetup-groups
service={{
endpoint: String
}}
/>
micro frontends
Coordinator
<calendar
service={{
endpoint: String
}}
/>
render
fetch→
services
/date/314
{
ref: /meetup/123,
starts: “Aug, 3rd”
}
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/217
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
persistence
Meetups calendar
<Meetup Groups> <Calendar>
frontend app
Interface QueryContext
{
term: string,
location: LatLng,
daterange: [Date,?Date]
tags: string[],
language: Lang,
user: UserContext
}
Interface QueryContext
{
term: string,
location: LatLng,
daterange: [Date,?Date]
tags: string[],
language: Lang,
user: UserContext
}
Host: meetups.{}
GET /search
{
query
}
<meetup-groups
service={{
endpoint: String
}}
/>
Host: calendar.{}
GET /search
{
context
}
micro frontends
<calendar
service={{
endpoint: String
}}
/>
fetch
query→
services
persistence
Meetups calendar
<Meetup Groups> <Calendar>
frontend app
Host: meetups.{}
GET /search
{
query
}
200 OK
{
groups: {
title: ‘coding leipzig’,
events: […],
}
}
Host: calendar.{}
GET /search
{
context
}
200 OK
{
groups: {
title: ‘coding leipzig’,
events: […],
}
}
micro frontends
Respond
render→
services
/date/314
{
ref: /meetup/123,
starts: “Aug, 3rd”
}
/meetup/123
{
title: “summer hack”,
starts: “Aug, 3rd”
}
/groups/217
{
title: “coding leipzig”,
town: “Leipzig”,
center: LatLng
}
persistence
Meetups calendar
<Meetup Groups> <Calendar>
frontend app
GET /
Interface QueryContext
{
term: string,
location: LatLng,
daterange: [Date,?Date]
tags: string[],
language: Lang,
user: UserContext
}
Interface QueryContext
{
term: string,
location: LatLng,
daterange: [Date,?Date]
tags: string[],
language: Lang,
user: UserContext
}
Host: meetups.{}
GET /search
{
query
}
200 OK
{
groups: {
title: ‘coding leipzig’,
events: […],
}
}
200 OK
{
“meetups.”: {
endpoint: ‘meetups.’,
<metadata>
},
“calendar.”: {},
...
}
<meetup-groups
service={{
endpoint: String
}}
/>
Host: calendar.{}
GET /search
{
context
}
200 OK
{
groups: {
title: ‘coding leipzig’,
events: […],
}
}
micro frontends
Coordinator
elasticsearch
<calendar
service={{
endpoint: String
}}
/>
Deployments
Good luck getting this one right!
- many languages supported
- Go, Java, Node, PHP, Python, Ruby, Lisp, C#/.NET
- lots of resource services
- Elasticsearch, MongoDB, Maria, Postgres, Solr
- Redis, RabbitMQ, Varnish, InfluxDB, Kafka
- declarative service definitions
- apps services↔ services
- post deploy tasks
- scheduled tasks
- routing
- scale
You could use Kubernetes (or Heroku) but it’s way harder to setup &
maintain
https://{default}/":
type: upstream
upstream: "frontend:http"
"https://www.{default}/":
type: redirect
to: "https://{default}/"
"https://coordinator.
{default}/":
type: upstream
upstream: "coordinator:http"
"https://rssreader.
{default}/":
type: upstream
upstream: "rssreader:http"
"https://calendar.{default}/":
type: upstream
upstream: "calendar:http"
"https://tweets.{default}/":
type: upstream
upstream: "tweets:http"
elasticsearch:
type:
elasticsearch:6.5
disk: 1024
size: S
kafkastreaming:
type: kafka:2.2
disk: 1024
name: coordinator
type: nodejs:10
size: S
dependencies:
nodejs:
pm2: "^2.4.0"
web:
commands:
start: "PM2_HOME=$PLATFORM_APP_DIR/run pm2 start index.js --no-daemon"
locations:
"/public":
passthru: false
root: "public"
headers:
Access-Control-Allow-Origin: "*"
Access-Control-Allow-Methods: "GET, POST, PUT, DELETE, PATCH,
OPTIONS"
Access-Control-Allow-Headers: "X-Requested-With, content-type,
Authorization"
allow: true
rules:
.(css|js|gif|jpe?g|png|ttf|eot|woff2?|otf|html|ico|svg|json?)$:
allow: true
^/robots.txt$:
allow: true
relationships:
kafka: "kafkastreaming:kafka"
mounts:
"/run": "shared:files/run"
disk: 128
- deploys an isolated environment for every branch
- copies all services & data from parent environment
- on merge, all new resources are created on the parent
environment
onboarding couldn’t be simpler
local environment only needed for convenience
access your environment’s remote services with SSH tunnels
Much faster / flexible local development
Must be customized for your local setup (user rights, ports)
define one service for each app
Reuse base images / Dockerfiles
Use microservices when
- you need a distributed architecture
- you can foresee decoupled development teams
- you can afford the deployment and maintenance process
- and use really good tools for it
- you are aware that there’s no shared data
- you’re on your way to serverless architectures
- you want to go maximum polyglot
https://github.com/cod1ng-earth/coding-earth
https://hub.coding.earth/
https://platform.sh/
https://microservices.io
@stadolf
Thank You!
please contribute :)
cod1ng-earth/coding-earth

A micro service story

  • 1.
  • 2.
    What’s the perfectwebsite for a coder community?
  • 3.
    - everybody cancontribute - home page: Welcome! Who are we? - event pages: when do we meet / announcements - services: TechRadar, forum, messaging - apis: extension points for other communities - content for coders! What’s the perfect website for a coder community?
  • 4.
  • 5.
    When I postan URL coding.earth shall determine if it can handle it extract content (find related content) (tag the content (automatically?) ) index the content display the new content. When I search for “ruby” on coding.earth It shall ask the index for “ruby” content (apply location / personal / date context) adjust the page accordingly
  • 6.
    The naīve approach Asynchronous http web service
  • 7.
    https://meetup.com/colei/events/123+ Add persistence Meetups elasticsearch POST url { url:https://meetup.com/colei/events/123 } services The naīve way Service “gateway” chooses the right service to talk to TweetsArticles
  • 8.
    https://meetup.com/colei/eve+ GO persistence elasticsearch services Thenaīve way Service “gateway” chooses the right service to talk to https://meetup.com/colei/events/123 Meetups TweetsArticles
  • 9.
    /meetup/123 { title: “summer hack”, starts:“Aug, 3rd” } /groups/colei { title: “coding leipzig”, town: “Leipzig”, center: LatLng } https://meetup.com/colei/eve+ GO persistence elasticsearch services The naīve way Service “gateway” chooses the right service to talk to https://meetup.com/colei/events/123 Meetups TweetsArticles
  • 10.
    /meetup/123 { title: “summer hack”, starts:“Aug, 3rd” } /groups/colei { title: “coding leipzig”, town: “Leipzig”, center: LatLng } any url here+ GO persistence elasticsearch New meetup: “Summer hack” services The naīve way Service “gateway” chooses the right service to talk to Your url has been processed Meetups
  • 11.
    /meetup/123 { title: “summer hack”, starts:“Aug, 3rd” } /groups/colei { title: “coding leipzig”, town: “Leipzig”, center: LatLng } any url here+ GO persistence elasticsearch POST url { url: https://meetup.com/colei/events/123 } New meetup: “Summer hack” services The naīve way Service “gateway” chooses the right service to talk to Your url has been processed Meetups
  • 12.
    /meetup/123 { title: “summer hack”, starts:“Aug, 3rd” } /groups/colei { title: “coding leipzig”, town: “Leipzig”, center: LatLng } any url here+ GO persistence elasticsearch POST url { url: https://meetup.com/colei/events/123 } New meetup: “Summer hack” services Service “gateway” chooses the right service to talk to Your url has been processed Meetups The naīve approach A synchronous http web service - requests keep synchonously running - any failure in any service can break the request - strong coupling between “request” and service layer - the gateway must choose an interface understood by all services - all services are chain-requested → hard to scale → what if more than 1 service can respond?
  • 13.
    The micro serviceway event driven services 1. adding data
  • 14.
    Coordinator server side discovery persistence Eventstream elasticsearch client event stream services https://meetup.com/colei/events/123+ GO
  • 15.
    Coordinator any url here+GO persistence Event stream elasticsearch NewUrl https://meetup.com/colei/events/123 client event stream services +client request id Http event→ POST url { url: https://meetup.com/colei/events/123 }
  • 16.
    /meetup/123 { title: “summer hack”, starts:“Aug, 3rd” } /groups/217 { title: “coding leipzig”, town: “Leipzig”, center: LatLng } Coordinator https://meetup.com/colei/events/123+ GO persistence Event stream Meetup.com elasticsearch services NewUrl https://meetup.com/colei/events/123 client event stream POST url { url: https://meetup.com/colei/events/123 } Event service→ index→ mediumtwitter youtube
  • 17.
    /meetup/123 { title: “summer hack”, starts:“Aug, 3rd” } /groups/217 { title: “coding leipzig”, town: “Leipzig”, center: LatLng } Coordinator persistence Event stream Meetup.com NewContent /meetup/123 Content: {...} interface: date elasticsearch services idx service→ event→ https://meetup.com/colei/events/123+ GO
  • 18.
    Coordinator persistence Event stream NewContent /meetup/123 Content: {...} interface:date elasticsearch New meetup: “Summer hack” client event stream services +client request id url has been processed client request id → event frontend→ https://meetup.com/colei/events/123+ GO
  • 19.
    /date/314 { ref: /meetup/123, starts: “Aug,3rd” } persistence Event stream NewContent /meetup/123 Content: {...} interface: date calendar elasticsearch NewContent /date/312 interface: date services event service→ index→ https://meetup.com/colei/events/123+ GO Coordinator
  • 20.
    Coordinator persistence Event stream elasticsearch client eventstream services New calendar entry “Summer hack” → event frontend→ https://meetup.com/colei/events/123+ GO NewContent /date/312 interface: date
  • 21.
    /date/314 { ref: /meetup/123, starts: “Aug,3rd” } /meetup/123 { title: “summer hack”, starts: “Aug, 3rd” } /groups/217 { title: “coding leipzig”, town: “Leipzig”, center: LatLng } Coordinator eventbrite persistence Event stream Meetup.comtwitter NewContent /meetup/123 Content: {...} interface: date calendar elasticsearch NewContent /date/312 interface: date client event stream New meetup: “Summer hack” services New calendar entry “Summer hack” +client request id Your url has been processed client request id NewUrl https://meetup.com/colei/events/123 POST url { url: https://meetup.com/colei/events/123 } https://meetup.com/colei/events/123+ GO
  • 22.
    The micro serviceway event driven services 2. getting data
  • 23.
    services persistence frontend app GET / 200OK { “meetups.”: { endpoint: ‘meetups.’, <metadata> }, “calendar.”: {}, ... } micro frontends Coordinator Service discovery
  • 24.
    services persistence <Meetup Groups> <Calendar> frontendapp 200 OK { “meetups.”: { endpoint: ‘meetups.’, <metadata> }, “calendar.”: {}, ... } <meetup-groups service={{ endpoint: String }} /> micro frontends Coordinator <calendar service={{ endpoint: String }} /> render fetch→
  • 25.
    services /date/314 { ref: /meetup/123, starts: “Aug,3rd” } /meetup/123 { title: “summer hack”, starts: “Aug, 3rd” } /groups/217 { title: “coding leipzig”, town: “Leipzig”, center: LatLng } persistence Meetups calendar <Meetup Groups> <Calendar> frontend app Interface QueryContext { term: string, location: LatLng, daterange: [Date,?Date] tags: string[], language: Lang, user: UserContext } Interface QueryContext { term: string, location: LatLng, daterange: [Date,?Date] tags: string[], language: Lang, user: UserContext } Host: meetups.{} GET /search { query } <meetup-groups service={{ endpoint: String }} /> Host: calendar.{} GET /search { context } micro frontends <calendar service={{ endpoint: String }} /> fetch query→
  • 26.
    services persistence Meetups calendar <Meetup Groups><Calendar> frontend app Host: meetups.{} GET /search { query } 200 OK { groups: { title: ‘coding leipzig’, events: […], } } Host: calendar.{} GET /search { context } 200 OK { groups: { title: ‘coding leipzig’, events: […], } } micro frontends Respond render→
  • 27.
    services /date/314 { ref: /meetup/123, starts: “Aug,3rd” } /meetup/123 { title: “summer hack”, starts: “Aug, 3rd” } /groups/217 { title: “coding leipzig”, town: “Leipzig”, center: LatLng } persistence Meetups calendar <Meetup Groups> <Calendar> frontend app GET / Interface QueryContext { term: string, location: LatLng, daterange: [Date,?Date] tags: string[], language: Lang, user: UserContext } Interface QueryContext { term: string, location: LatLng, daterange: [Date,?Date] tags: string[], language: Lang, user: UserContext } Host: meetups.{} GET /search { query } 200 OK { groups: { title: ‘coding leipzig’, events: […], } } 200 OK { “meetups.”: { endpoint: ‘meetups.’, <metadata> }, “calendar.”: {}, ... } <meetup-groups service={{ endpoint: String }} /> Host: calendar.{} GET /search { context } 200 OK { groups: { title: ‘coding leipzig’, events: […], } } micro frontends Coordinator elasticsearch <calendar service={{ endpoint: String }} />
  • 28.
  • 30.
    - many languagessupported - Go, Java, Node, PHP, Python, Ruby, Lisp, C#/.NET - lots of resource services - Elasticsearch, MongoDB, Maria, Postgres, Solr - Redis, RabbitMQ, Varnish, InfluxDB, Kafka - declarative service definitions - apps services↔ services - post deploy tasks - scheduled tasks - routing - scale You could use Kubernetes (or Heroku) but it’s way harder to setup & maintain
  • 31.
    https://{default}/": type: upstream upstream: "frontend:http" "https://www.{default}/": type:redirect to: "https://{default}/" "https://coordinator. {default}/": type: upstream upstream: "coordinator:http" "https://rssreader. {default}/": type: upstream upstream: "rssreader:http" "https://calendar.{default}/": type: upstream upstream: "calendar:http" "https://tweets.{default}/": type: upstream upstream: "tweets:http" elasticsearch: type: elasticsearch:6.5 disk: 1024 size: S kafkastreaming: type: kafka:2.2 disk: 1024
  • 32.
    name: coordinator type: nodejs:10 size:S dependencies: nodejs: pm2: "^2.4.0" web: commands: start: "PM2_HOME=$PLATFORM_APP_DIR/run pm2 start index.js --no-daemon" locations: "/public": passthru: false root: "public" headers: Access-Control-Allow-Origin: "*" Access-Control-Allow-Methods: "GET, POST, PUT, DELETE, PATCH, OPTIONS" Access-Control-Allow-Headers: "X-Requested-With, content-type, Authorization" allow: true rules: .(css|js|gif|jpe?g|png|ttf|eot|woff2?|otf|html|ico|svg|json?)$: allow: true ^/robots.txt$: allow: true relationships: kafka: "kafkastreaming:kafka" mounts: "/run": "shared:files/run" disk: 128
  • 33.
    - deploys anisolated environment for every branch - copies all services & data from parent environment - on merge, all new resources are created on the parent environment onboarding couldn’t be simpler local environment only needed for convenience access your environment’s remote services with SSH tunnels
  • 34.
    Much faster /flexible local development Must be customized for your local setup (user rights, ports) define one service for each app Reuse base images / Dockerfiles
  • 36.
    Use microservices when -you need a distributed architecture - you can foresee decoupled development teams - you can afford the deployment and maintenance process - and use really good tools for it - you are aware that there’s no shared data - you’re on your way to serverless architectures - you want to go maximum polyglot
  • 37.
  • 38.
    Thank You! please contribute:) cod1ng-earth/coding-earth