SlideShare a Scribd company logo
(MICRO?) SERVICES
ARCHITECTURE IN PRACTICE
ADAM POLAK
HEAD OF NODE.JS
ADAM.POLAK@TSH.IO
POLAK.ADAM1
THEORY
▸ SPECIALIZED SMALL APPS
▸ SEPARATE REPOS
▸ OWN STRUCTURE, API AND LANGUAGE
▸ INDEPENDENT BUILDS, TESTS AND DEPLOYS
▸ EASY TO MAINTAIN BECAUSE OF SMALL CODE
BASE
THEORY
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
A BRAND NEW
PROJECT
▸ 5 SERVICES AT START
▸ SPA
▸ 6-7 DEVELOPERS AT START
PROJECT
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
▸ WHAT ABOUT REPOSITORIES?
▸ WHAT ABOUT API?
▸ WHAT ABOUT SETTING UP NEW SERVICE?
▸ WHAT ABOUT DEVELOPMENT?
▸ WHAT ABOUT TESTS?
▸ AND MANY MANY MORE
QUESTIONS?
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
MULTIPLE
REPOSITORIES
SPA API
Frontend Backend
USE CASE #1
USE CASE SUMARY
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
▸ 2 “SERVICES” - SPA + API
▸ SEPARATE TEAMS
▸ DIFFERENT PROJECT STRUCTURE
▸ TECH PER PROJECT
▸ PRACTICES PER PROJECT
LET’S SCALE
IT UP!
GATEWAY
PAYMENT
USE CASE #2 - BACKEND
REPORTINGSECURITYSEARCH
USE CASE #2 - FRONTEND
SPA GATEWAY
THEORY VS REALITY
GATEWAY
NODE.JS
1 DEV
SECURITY
PHP
1 DEV
REPORTING
PHP
1 DEV
PAYMENT
.NET
1 DEV
SEARCH
PYTHON
1 DEV
SPA
REACT
1 DEV
THEORY
▸ BEST AVAILABLE TECH FOR GIVEN TASK
▸ ???
PROS
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
▸ 6 DIFFERENT TECH STACKS
▸ NO SHARED CODE = DUPLICATION
▸ 6 DIFFERENT BUILD/DEPLOY PROCESSES
▸ MUCH HARDER TO MOVE DEVS BETWEEN
PROJECTS
CONS
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
SPA
REALITY
SHARED
NODE.JS
CODE
SHARED
PHP
CODE
3 DEVS 2 DEVS 1 DEV
REACT
PHPNODE.JS
GATEWAYREPORTINGSEARCH SECURITYPAYMENT
▸ LOWER NUMBER OF TECHS = EASIER
MAINTANANCE
▸ SHARED CODE BETWEEN SAME TECH PROJECTS
▸ SHARED BUILD/DEPLOY PROCESS BETWEEN SAME
TECH
▸ SHARED STRUCTURE BETWEEN SAME TECH
PROJECTS
▸ YOU CAN MOVE FREELY BETWEEN PROJECTS OF
SAME TECH
PROS
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
▸ LOWER PERFORMANCE = WE CANNOT USE THE
BEST POSSIBLE TOOLS
▸ HARDER TO KEEP IT UP TO DATE
CONS
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
GATEWAY
REPORTING
SEARCH
SECURITY
PAYMENT
SPA
TSH REALITY = MONOREPO
3 DEVS 2 DEVS 1 DEVS
REACTPHPNODE.JS
▸ EASIEST TO DEVELOP
▸ STILL MODULAR
PROS
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
▸ LARGER CODE BASE
▸ HARDER TO KEEP BOUNDARIES
CONS
THE MOST BORING PART
OF A NEW SERVICE?
PLANNING?
ARCHITECTURE?
SERVICE
BOILERPLATE!
security
src
app
grpc
proto
service
container.ts
index.ts
SERVICE SETUP
SAME STRUCTURE
FOR EVERY
SERVICE
+
CUSTOM
MADE
OR
SERVICE
GENERATOR
= SECURITY
SERVICE
SETTING UP SERVICE AT TSH
tools create-service —project-name security
/services
/security
/src
/app
/grpc
/proto
/service
container.ts
index.ts
FOCUS ON BUSINESS LOGIC
NOT SERVICE STRUCTURE
WHAT’S NEXT?
SERVICE NEEDS
A CONTRACT
POSSIBLE SOLUTIONS
OUR CHOICE
WHY DO WE NEED IT?
TO GENERATE THINGS!
▸ CLIENTS
▸ TYPES
▸ DTO’S
▸ COMMANDS / HANDLERS / ROUTES SKELETONS
THINGS GENERATED FROM CONTRACTS
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
GENERATING FROM PROTO AT TSH
syntax = "proto3";
import "messages/send-notification.proto";
import "messages/acknowledge-notification.proto";
import "messages/get-notifications.proto";
import "messages/get-notification.proto";
package notifications;
service NotificationsService {
rpc sendNotification (SendNotificationRequest) returns (SendNotificationResponse) {}
rpc acknowledgeNotification (AcknowledgeNotificationRequest) returns (AcknowledgeNotificationResponse) {}
rpc getNotifications (GetNotificationsRequest) returns (GetNotificationsResponse) {}
rpc getNotification (GetNotificationRequest) returns (GetNotificationResponse) {}
}
GENERATING FROM PROTO AT TSH
tools from-proto —service-name security --generator-type=client 
/services
/security
/src
/grpc
/client
index.ts
/types
index.ts
index.ts
/proto
/service
service.ts
tools from-proto —service-name security --generator-type=service 
GENERATING FROM PROTO AT TSH
/*****************************************/
/* THIS FILE WAS GENERATED */
/* DO NOT TOUCH */
/*****************************************/
import { Observable } from 'rxjs'
import * as grpc from 'grpc'
import { resolve } from 'path'
import { Logger, loadMessageDefinition, toPromise, toObservable } from 'brickvest-js-common'
import {
SendNotificationRequest,
SendNotificationResponse,
AcknowledgeNotificationRequest,
AcknowledgeNotificationResponse,
GetNotificationsRequest,
GetNotificationsResponse,
GetNotificationRequest,
GetNotificationResponse,
GetUnacknowledgedNotificationsCountRequest,
GetUnacknowledgedNotificationsCountResponse,
NotificationStreamToService,
NotificationStreamToClient,
} from '../types'
type ClientDependencies = {
uri: string
logger: Logger
}
class NotificationsClient {
private client: any
constructor(private dependencies: ClientDependencies) {
const messageDefinition = loadMessageDefinition(resolve(__dirname, '../../proto/notifications-service.proto'))
const proto: any = grpc.loadPackageDefinition(messageDefinition).notifications
this.client = new proto.NotificationsService(dependencies.uri, grpc.credentials.createInsecure())
}
public sendNotification(request: SendNotificationRequest): Promise<SendNotificationResponse> {
this.dependencies.logger.info(
`Notifications service client handling: sendNotification (${JSON.stringify(request)})`
)
return toPromise<SendNotificationRequest, SendNotificationResponse>(this.client, 'sendNotification')(request)
}
public acknowledgeNotification(request: AcknowledgeNotificationRequest): Promise<AcknowledgeNotificationResponse> {
this.dependencies.logger.info(
`Notifications service client handling: acknowledgeNotification (${JSON.stringify(request)})`
)
return toPromise<AcknowledgeNotificationRequest, AcknowledgeNotificationResponse>(
this.client,
'acknowledgeNotification'
)(request)
}
public getNotifications(request: GetNotificationsRequest): Promise<GetNotificationsResponse> {
this.dependencies.logger.info(
`Notifications service client handling: getNotifications (${JSON.stringify(request)})`
)
return toPromise<GetNotificationsRequest, GetNotificationsResponse>(this.client, 'getNotifications')(request)
}
public getNotification(request: GetNotificationRequest): Promise<GetNotificationResponse> {
this.dependencies.logger.info(`Notifications service client handling: getNotification (${JSON.stringify(request)})`)
return toPromise<GetNotificationRequest, GetNotificationResponse>(this.client, 'getNotification')(request)
}
public getUnacknowledgedNotificationsCount(
request: GetUnacknowledgedNotificationsCountRequest
): Promise<GetUnacknowledgedNotificationsCountResponse> {
this.dependencies.logger.info(
`Notifications service client handling: getUnacknowledgedNotificationsCount (${JSON.stringify(request)})`
)
return toPromise<GetUnacknowledgedNotificationsCountRequest, GetUnacknowledgedNotificationsCountResponse>(
this.client,
'getUnacknowledgedNotificationsCount'
)(request)
}
public notificationsStream(request: NotificationStreamToService): Promise<Observable<NotificationStreamToClient>> {
this.dependencies.logger.info(
`Notifications service client handling: notificationsStream (${JSON.stringify(request)})`
)
return toObservable<NotificationStreamToService, NotificationStreamToClient>(this.client, 'notificationsStream')(
request
)
}
}
export { NotificationsClient }
KEEP SAME CODE STYLE
AND USE STATIC ANALYSIS
TOOLS
(Micro?)services architecture in practice
LOCAL DEVELOPMENT
DOCKER
&
DOCKER-COMPOSE
DOCKERFILE
FROM node:10.13-alpine
ARG NPM_TOKEN
RUN echo “registry=https://private-repository.com/repository/npm/“ > /root/.npmrc;
RUN echo "always-auth=true" >> /root/.npmrc;
RUN echo "_auth=${NPM_TOKEN}" >> /root/.npmrc;
WORKDIR /dir
COPY . .
RUN npm config set unsafe-perm true
RUN apk add bash
RUN npm install ts-node ts-node-dev typescript -g
RUN npm i
EXPOSE 50050
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
SINGLE IMAGE
MULTIPLE ENTRYPOINTS
DOCKER-COMPOSE
messaging:
image: our-app:latest
container_name: messaging
command: [bash, -c, 'ts-node-dev --poll ./services/messaging/src/index.ts’]
hostname:messaging
volumes: *volumes
depends_on:
- data
gateway:
image: our-app:latest
container_name: gateway
command: [bash, -c, 'ts-node-dev --poll ./services/gateway/src/index.ts']
hostname: gateway
volumes: *volumes
depends_on:
- messaging
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
YAML ANCHORS AND REFERENCES TO KEEP IT CLEAN
js:
image: our-app:latest
build:
context: .
dockerfile: docker/dev/Dockerfile
args:
- NPM_TOKEN
volumes: &volumes
- ./:/dir
- /dir/build/
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
WHAT ABOUT TESTS?
TESTING TROPHY
TESTS AS DOCKER CONTAINER
integration-tests:
image: our-app:latest
command:
[
'bash',
'-c',
‘./docker/wait.sh ldap-service 636 ./docker/wait.sh security 50050 npm run integration-tests',
]
volumes: *volumes
depends_on:
- messaging
- security
- ui-gateway
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
INTEGRATION TESTS
ARE EASY
BUT BEWARE OF
ASYNC
WHAT’S WRONG ON THIS PICTURE?
test('Gateway - workflow - create user test', { timeout: 10000 }, async (t: test.Test) => {
const clientSocket = await connectors.getConnector(config.clients.socketIO.type, config.clients.socketIO)
const validateMessage = createSocketValidator(clientSocket)
const connect$ = fromEvent(clientSocket, 'connect')
const mailerClientConfig = config.clients.mailerClient
const emailCountBeforeSending = await getCurrentEmailsCount(mailerClientConfig.mailHogUri)
connect$.subscribe(async () => {
(…)
t.end();
});
});
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
WHAT’S WRONG ON THIS PICTURE?
test('Gateway - workflow - create user test', { timeout: 10000 }, async (t: test.Test) => {
const clientSocket = await connectors.getConnector(config.clients.socketIO.type, config.clients.socketIO)
const validateMessage = createSocketValidator(clientSocket)
const connect$ = fromEvent(clientSocket, 'connect')
const mailerClientConfig = config.clients.mailerClient
const emailCountBeforeSending = await getCurrentEmailsCount(mailerClientConfig.mailHogUri)
connect$.subscribe(async () => {
(…)
t.end();
});
});
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
WHAT ?!
SOLUTION
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
test('Gateway - workflow - create user test', { timeout: 10000 }, async (t: test.Test) => {
const mailerClientConfig = config.clients.mailerClient
const emailCountBeforeSending = await getCurrentEmailsCount(mailerClientConfig.mailHogUri)
const clientSocket = await connectors.getConnector(config.clients.socketIO.type, config.clients.socketIO)
const validateMessage = createSocketValidator(clientSocket)
const connect$ = fromEvent(clientSocket, 'connect')
connect$.subscribe(async () => {
(…)
t.end();
});
});
WHAT ABOUT
NEGATIVE PATHS?
YOU CAN CONTROL
SERVICES DIRECTLY
FROM TESTS
PASS DOCKER SOCKET TO CONTAINER
js:
image: our-app:latest
build:
context: .
dockerfile: docker/dev/Dockerfile
args:
- NPM_TOKEN
volumes: &volumes
- ./:/dir
- /dir/build/
- /var/run/docker.sock:/var/run/docker.sock
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
CONTROL CONTAINERS FROM TESTS CONTAINER
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
test('Security integration tests - should reconnect to security service', async (t: test.Test) => {
pause(config.containers.security.name)
const payload = {…}
const authenticate = apiGatewayClient.proxy({
payload,
method: 'authenticate',
service: 'security'
})
const timedOut = await Promise.race([waitForTimeout, authenticate])
timedOut === true ? t.pass() : t.fail()
await ensureAreUpAndUnpaused([config.containers.security])
t.end()
})
CONTROL CONTAINERS FROM TESTS CONTAINER
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
test('Security integration tests - should reconnect to security service', async (t: test.Test) => {
pause(config.containers.security.name)
const payload = {…}
const authenticate = apiGatewayClient.proxy({
payload,
method: 'authenticate',
service: 'security'
})
const timedOut = await Promise.race([waitForTimeout, authenticate])
timedOut === true ? t.pass() : t.fail()
await ensureAreUpAndUnpaused([config.containers.security])
t.end()
})
CONTROL CONTAINERS FROM TESTS CONTAINER
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
export const ensureAreUpAndUnpaused = (containers: ContainerConfig[]): Promise<any> => {
const upOrUnpause: Array<Promise<any>> = []
containers.forEach(container => {
if (isPaused(container.name)) {
upOrUnpause.push(unpause(container))
} else if (isDown(container.name)) {
upOrUnpause.push(up(container))
}
})
return Promise.all(upOrUnpause)
}
export const pause = (containerName: string) => shell.exec(`docker pause ${containerName}`)
▸ MONOREPO IS EASIER TO DEVELOP WITH
▸ AUTOMATE PROJECT CREATION
▸ USE CONTRACTS AND GENERATE CODE FROM
THEM
▸ USE DOCKER
▸ FOCUS ON INTEGRATION TESTS
▸ TEST NEGATIVE PATHS
TL/DR
(MICRO?) SERVICES ARCHITECTURE IN PRACTICE
QUESTIONS ?
THANK YOU

More Related Content

PDF
Ephemeral DevOps: Adventures in Managing Short-Lived Systems
PPTX
Open Source Power Tools - Opensouthcode 2018-06-02
PPTX
AzureDay Kyiv 2016 Release Management
PDF
Multilanguage Pipelines with Jenkins, Docker and Kubernetes (Oracle Code One ...
PDF
Dev ops with smell v1.2
PDF
Multilanguage Pipelines with Jenkins, Docker and Kubernetes (Commit Conf 2018)
PDF
"Design First" APIs with Swagger
PDF
Zero-downtime deployment of Micro-services with Kubernetes
Ephemeral DevOps: Adventures in Managing Short-Lived Systems
Open Source Power Tools - Opensouthcode 2018-06-02
AzureDay Kyiv 2016 Release Management
Multilanguage Pipelines with Jenkins, Docker and Kubernetes (Oracle Code One ...
Dev ops with smell v1.2
Multilanguage Pipelines with Jenkins, Docker and Kubernetes (Commit Conf 2018)
"Design First" APIs with Swagger
Zero-downtime deployment of Micro-services with Kubernetes

Similar to (Micro?)services architecture in practice (20)

PPTX
Full stack development best practice and toolset
PDF
Why Kubernetes? Cloud Native and Developer Experience at Zalando - OWL Tech &...
PDF
Android 103 - Firebase and Architecture Components
PDF
Enterprise State Management with NGRX/platform
PDF
Application Modernisation with PKS
PDF
Application Modernisation with PKS
PPTX
Developer Intro to OpenShift
PDF
Developer intro to open shift
PDF
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
PPTX
CQRS and Event Sourcing
PPTX
App Mod 02: A developer intro to open shift
PDF
Velocity NY 2016 - Devops: Who Does What?
PDF
Cloud-native .NET Microservices mit Kubernetes
PDF
Openshift Container Platform: First ItalyMeetup
PDF
IBM Cloud University: Build, Deploy and Scale Node.js Microservices
PDF
Improving velocity through abstraction
PDF
Faster, more Secure Application Modernization and Replatforming with PKS - Ku...
PPTX
CI/CD and TDD in deploying kamailio
PDF
Live Coding 12 Factor App
PPTX
Serverless survival kit
Full stack development best practice and toolset
Why Kubernetes? Cloud Native and Developer Experience at Zalando - OWL Tech &...
Android 103 - Firebase and Architecture Components
Enterprise State Management with NGRX/platform
Application Modernisation with PKS
Application Modernisation with PKS
Developer Intro to OpenShift
Developer intro to open shift
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
CQRS and Event Sourcing
App Mod 02: A developer intro to open shift
Velocity NY 2016 - Devops: Who Does What?
Cloud-native .NET Microservices mit Kubernetes
Openshift Container Platform: First ItalyMeetup
IBM Cloud University: Build, Deploy and Scale Node.js Microservices
Improving velocity through abstraction
Faster, more Secure Application Modernization and Replatforming with PKS - Ku...
CI/CD and TDD in deploying kamailio
Live Coding 12 Factor App
Serverless survival kit
Ad

More from The Software House (20)

PDF
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
PDF
Uszanowanko Podsumowanko
PDF
Jak efektywnie podejść do certyfikacji w AWS?
PDF
O co chodzi z tą dostępnością cyfrową?
PDF
Chat tekstowy z użyciem Amazon Chime
PDF
Migracje danych serverless
PDF
Jak nie zwariować z architekturą Serverless?
PDF
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
PDF
Feature flags na ratunek projektu w JavaScript
PDF
Typowanie nominalne w TypeScript
PDF
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
PDF
Serverless Compose vs hurtownia danych
PDF
Testy API: połączenie z bazą danych czy implementacja w pamięci
PDF
Jak skutecznie read model. Case study
PDF
Firestore czyli ognista baza od giganta z Doliny Krzemowej
PDF
Jak utrzymać stado Lambd w ryzach
PDF
Jak poskromić AWS?
PDF
O łączeniu Storyblok i Next.js
PDF
Amazon Step Functions. Sposób na implementację procesów w chmurze
PDF
Od Figmy do gotowej aplikacji bez linijki kodu
Jak kraść miliony, czyli o błędach bezpieczeństwa, które mogą spotkać również...
Uszanowanko Podsumowanko
Jak efektywnie podejść do certyfikacji w AWS?
O co chodzi z tą dostępnością cyfrową?
Chat tekstowy z użyciem Amazon Chime
Migracje danych serverless
Jak nie zwariować z architekturą Serverless?
Analiza semantyczna artykułów prasowych w 5 sprintów z użyciem AWS
Feature flags na ratunek projektu w JavaScript
Typowanie nominalne w TypeScript
Automatyzacja tworzenia frontendu z wykorzystaniem GraphQL
Serverless Compose vs hurtownia danych
Testy API: połączenie z bazą danych czy implementacja w pamięci
Jak skutecznie read model. Case study
Firestore czyli ognista baza od giganta z Doliny Krzemowej
Jak utrzymać stado Lambd w ryzach
Jak poskromić AWS?
O łączeniu Storyblok i Next.js
Amazon Step Functions. Sposób na implementację procesów w chmurze
Od Figmy do gotowej aplikacji bez linijki kodu
Ad

Recently uploaded (20)

PPTX
Power Point - Lesson 3_2.pptx grad school presentation
PDF
WebRTC in SignalWire - troubleshooting media negotiation
PDF
Sims 4 Historia para lo sims 4 para jugar
PDF
Paper PDF World Game (s) Great Redesign.pdf
PPTX
INTERNET------BASICS-------UPDATED PPT PRESENTATION
PPTX
CHE NAA, , b,mn,mblblblbljb jb jlb ,j , ,C PPT.pptx
PPTX
introduction about ICD -10 & ICD-11 ppt.pptx
PDF
How to Ensure Data Integrity During Shopify Migration_ Best Practices for Sec...
PPTX
Module 1 - Cyber Law and Ethics 101.pptx
PPTX
Funds Management Learning Material for Beg
PPTX
SAP Ariba Sourcing PPT for learning material
PPTX
June-4-Sermon-Powerpoint.pptx USE THIS FOR YOUR MOTIVATION
PPT
Design_with_Watersergyerge45hrbgre4top (1).ppt
PDF
Slides PDF The World Game (s) Eco Economic Epochs.pdf
PDF
Unit-1 introduction to cyber security discuss about how to secure a system
PPTX
Job_Card_System_Styled_lorem_ipsum_.pptx
PPTX
Internet___Basics___Styled_ presentation
PDF
The Internet -By the Numbers, Sri Lanka Edition
PDF
Best Practices for Testing and Debugging Shopify Third-Party API Integrations...
PDF
RPKI Status Update, presented by Makito Lay at IDNOG 10
Power Point - Lesson 3_2.pptx grad school presentation
WebRTC in SignalWire - troubleshooting media negotiation
Sims 4 Historia para lo sims 4 para jugar
Paper PDF World Game (s) Great Redesign.pdf
INTERNET------BASICS-------UPDATED PPT PRESENTATION
CHE NAA, , b,mn,mblblblbljb jb jlb ,j , ,C PPT.pptx
introduction about ICD -10 & ICD-11 ppt.pptx
How to Ensure Data Integrity During Shopify Migration_ Best Practices for Sec...
Module 1 - Cyber Law and Ethics 101.pptx
Funds Management Learning Material for Beg
SAP Ariba Sourcing PPT for learning material
June-4-Sermon-Powerpoint.pptx USE THIS FOR YOUR MOTIVATION
Design_with_Watersergyerge45hrbgre4top (1).ppt
Slides PDF The World Game (s) Eco Economic Epochs.pdf
Unit-1 introduction to cyber security discuss about how to secure a system
Job_Card_System_Styled_lorem_ipsum_.pptx
Internet___Basics___Styled_ presentation
The Internet -By the Numbers, Sri Lanka Edition
Best Practices for Testing and Debugging Shopify Third-Party API Integrations...
RPKI Status Update, presented by Makito Lay at IDNOG 10

(Micro?)services architecture in practice

  • 2. ADAM POLAK HEAD OF NODE.JS ADAM.POLAK@TSH.IO POLAK.ADAM1
  • 4. ▸ SPECIALIZED SMALL APPS ▸ SEPARATE REPOS ▸ OWN STRUCTURE, API AND LANGUAGE ▸ INDEPENDENT BUILDS, TESTS AND DEPLOYS ▸ EASY TO MAINTAIN BECAUSE OF SMALL CODE BASE THEORY (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 6. ▸ 5 SERVICES AT START ▸ SPA ▸ 6-7 DEVELOPERS AT START PROJECT (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 7. ▸ WHAT ABOUT REPOSITORIES? ▸ WHAT ABOUT API? ▸ WHAT ABOUT SETTING UP NEW SERVICE? ▸ WHAT ABOUT DEVELOPMENT? ▸ WHAT ABOUT TESTS? ▸ AND MANY MANY MORE QUESTIONS? (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 10. USE CASE SUMARY (MICRO?) SERVICES ARCHITECTURE IN PRACTICE ▸ 2 “SERVICES” - SPA + API ▸ SEPARATE TEAMS ▸ DIFFERENT PROJECT STRUCTURE ▸ TECH PER PROJECT ▸ PRACTICES PER PROJECT
  • 12. GATEWAY PAYMENT USE CASE #2 - BACKEND REPORTINGSECURITYSEARCH
  • 13. USE CASE #2 - FRONTEND SPA GATEWAY
  • 15. GATEWAY NODE.JS 1 DEV SECURITY PHP 1 DEV REPORTING PHP 1 DEV PAYMENT .NET 1 DEV SEARCH PYTHON 1 DEV SPA REACT 1 DEV THEORY
  • 16. ▸ BEST AVAILABLE TECH FOR GIVEN TASK ▸ ??? PROS (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 17. ▸ 6 DIFFERENT TECH STACKS ▸ NO SHARED CODE = DUPLICATION ▸ 6 DIFFERENT BUILD/DEPLOY PROCESSES ▸ MUCH HARDER TO MOVE DEVS BETWEEN PROJECTS CONS (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 18. SPA REALITY SHARED NODE.JS CODE SHARED PHP CODE 3 DEVS 2 DEVS 1 DEV REACT PHPNODE.JS GATEWAYREPORTINGSEARCH SECURITYPAYMENT
  • 19. ▸ LOWER NUMBER OF TECHS = EASIER MAINTANANCE ▸ SHARED CODE BETWEEN SAME TECH PROJECTS ▸ SHARED BUILD/DEPLOY PROCESS BETWEEN SAME TECH ▸ SHARED STRUCTURE BETWEEN SAME TECH PROJECTS ▸ YOU CAN MOVE FREELY BETWEEN PROJECTS OF SAME TECH PROS (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 20. ▸ LOWER PERFORMANCE = WE CANNOT USE THE BEST POSSIBLE TOOLS ▸ HARDER TO KEEP IT UP TO DATE CONS (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 21. GATEWAY REPORTING SEARCH SECURITY PAYMENT SPA TSH REALITY = MONOREPO 3 DEVS 2 DEVS 1 DEVS REACTPHPNODE.JS
  • 22. ▸ EASIEST TO DEVELOP ▸ STILL MODULAR PROS (MICRO?) SERVICES ARCHITECTURE IN PRACTICE ▸ LARGER CODE BASE ▸ HARDER TO KEEP BOUNDARIES CONS
  • 23. THE MOST BORING PART OF A NEW SERVICE?
  • 27. security src app grpc proto service container.ts index.ts SERVICE SETUP SAME STRUCTURE FOR EVERY SERVICE + CUSTOM MADE OR SERVICE GENERATOR = SECURITY SERVICE
  • 28. SETTING UP SERVICE AT TSH tools create-service —project-name security /services /security /src /app /grpc /proto /service container.ts index.ts
  • 29. FOCUS ON BUSINESS LOGIC NOT SERVICE STRUCTURE
  • 33. WHY DO WE NEED IT?
  • 35. ▸ CLIENTS ▸ TYPES ▸ DTO’S ▸ COMMANDS / HANDLERS / ROUTES SKELETONS THINGS GENERATED FROM CONTRACTS (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 36. GENERATING FROM PROTO AT TSH syntax = "proto3"; import "messages/send-notification.proto"; import "messages/acknowledge-notification.proto"; import "messages/get-notifications.proto"; import "messages/get-notification.proto"; package notifications; service NotificationsService { rpc sendNotification (SendNotificationRequest) returns (SendNotificationResponse) {} rpc acknowledgeNotification (AcknowledgeNotificationRequest) returns (AcknowledgeNotificationResponse) {} rpc getNotifications (GetNotificationsRequest) returns (GetNotificationsResponse) {} rpc getNotification (GetNotificationRequest) returns (GetNotificationResponse) {} }
  • 37. GENERATING FROM PROTO AT TSH tools from-proto —service-name security --generator-type=client  /services /security /src /grpc /client index.ts /types index.ts index.ts /proto /service service.ts tools from-proto —service-name security --generator-type=service 
  • 38. GENERATING FROM PROTO AT TSH /*****************************************/ /* THIS FILE WAS GENERATED */ /* DO NOT TOUCH */ /*****************************************/ import { Observable } from 'rxjs' import * as grpc from 'grpc' import { resolve } from 'path' import { Logger, loadMessageDefinition, toPromise, toObservable } from 'brickvest-js-common' import { SendNotificationRequest, SendNotificationResponse, AcknowledgeNotificationRequest, AcknowledgeNotificationResponse, GetNotificationsRequest, GetNotificationsResponse, GetNotificationRequest, GetNotificationResponse, GetUnacknowledgedNotificationsCountRequest, GetUnacknowledgedNotificationsCountResponse, NotificationStreamToService, NotificationStreamToClient, } from '../types' type ClientDependencies = { uri: string logger: Logger } class NotificationsClient { private client: any constructor(private dependencies: ClientDependencies) { const messageDefinition = loadMessageDefinition(resolve(__dirname, '../../proto/notifications-service.proto')) const proto: any = grpc.loadPackageDefinition(messageDefinition).notifications this.client = new proto.NotificationsService(dependencies.uri, grpc.credentials.createInsecure()) } public sendNotification(request: SendNotificationRequest): Promise<SendNotificationResponse> { this.dependencies.logger.info( `Notifications service client handling: sendNotification (${JSON.stringify(request)})` ) return toPromise<SendNotificationRequest, SendNotificationResponse>(this.client, 'sendNotification')(request) } public acknowledgeNotification(request: AcknowledgeNotificationRequest): Promise<AcknowledgeNotificationResponse> { this.dependencies.logger.info( `Notifications service client handling: acknowledgeNotification (${JSON.stringify(request)})` ) return toPromise<AcknowledgeNotificationRequest, AcknowledgeNotificationResponse>( this.client, 'acknowledgeNotification' )(request) } public getNotifications(request: GetNotificationsRequest): Promise<GetNotificationsResponse> { this.dependencies.logger.info( `Notifications service client handling: getNotifications (${JSON.stringify(request)})` ) return toPromise<GetNotificationsRequest, GetNotificationsResponse>(this.client, 'getNotifications')(request) } public getNotification(request: GetNotificationRequest): Promise<GetNotificationResponse> { this.dependencies.logger.info(`Notifications service client handling: getNotification (${JSON.stringify(request)})`) return toPromise<GetNotificationRequest, GetNotificationResponse>(this.client, 'getNotification')(request) } public getUnacknowledgedNotificationsCount( request: GetUnacknowledgedNotificationsCountRequest ): Promise<GetUnacknowledgedNotificationsCountResponse> { this.dependencies.logger.info( `Notifications service client handling: getUnacknowledgedNotificationsCount (${JSON.stringify(request)})` ) return toPromise<GetUnacknowledgedNotificationsCountRequest, GetUnacknowledgedNotificationsCountResponse>( this.client, 'getUnacknowledgedNotificationsCount' )(request) } public notificationsStream(request: NotificationStreamToService): Promise<Observable<NotificationStreamToClient>> { this.dependencies.logger.info( `Notifications service client handling: notificationsStream (${JSON.stringify(request)})` ) return toObservable<NotificationStreamToService, NotificationStreamToClient>(this.client, 'notificationsStream')( request ) } } export { NotificationsClient }
  • 39. KEEP SAME CODE STYLE AND USE STATIC ANALYSIS TOOLS
  • 43. DOCKERFILE FROM node:10.13-alpine ARG NPM_TOKEN RUN echo “registry=https://private-repository.com/repository/npm/“ > /root/.npmrc; RUN echo "always-auth=true" >> /root/.npmrc; RUN echo "_auth=${NPM_TOKEN}" >> /root/.npmrc; WORKDIR /dir COPY . . RUN npm config set unsafe-perm true RUN apk add bash RUN npm install ts-node ts-node-dev typescript -g RUN npm i EXPOSE 50050 (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 46. DOCKER-COMPOSE messaging: image: our-app:latest container_name: messaging command: [bash, -c, 'ts-node-dev --poll ./services/messaging/src/index.ts’] hostname:messaging volumes: *volumes depends_on: - data gateway: image: our-app:latest container_name: gateway command: [bash, -c, 'ts-node-dev --poll ./services/gateway/src/index.ts'] hostname: gateway volumes: *volumes depends_on: - messaging (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 47. YAML ANCHORS AND REFERENCES TO KEEP IT CLEAN js: image: our-app:latest build: context: . dockerfile: docker/dev/Dockerfile args: - NPM_TOKEN volumes: &volumes - ./:/dir - /dir/build/ (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 50. TESTS AS DOCKER CONTAINER integration-tests: image: our-app:latest command: [ 'bash', '-c', ‘./docker/wait.sh ldap-service 636 ./docker/wait.sh security 50050 npm run integration-tests', ] volumes: *volumes depends_on: - messaging - security - ui-gateway (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 53. WHAT’S WRONG ON THIS PICTURE? test('Gateway - workflow - create user test', { timeout: 10000 }, async (t: test.Test) => { const clientSocket = await connectors.getConnector(config.clients.socketIO.type, config.clients.socketIO) const validateMessage = createSocketValidator(clientSocket) const connect$ = fromEvent(clientSocket, 'connect') const mailerClientConfig = config.clients.mailerClient const emailCountBeforeSending = await getCurrentEmailsCount(mailerClientConfig.mailHogUri) connect$.subscribe(async () => { (…) t.end(); }); }); (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 54. WHAT’S WRONG ON THIS PICTURE? test('Gateway - workflow - create user test', { timeout: 10000 }, async (t: test.Test) => { const clientSocket = await connectors.getConnector(config.clients.socketIO.type, config.clients.socketIO) const validateMessage = createSocketValidator(clientSocket) const connect$ = fromEvent(clientSocket, 'connect') const mailerClientConfig = config.clients.mailerClient const emailCountBeforeSending = await getCurrentEmailsCount(mailerClientConfig.mailHogUri) connect$.subscribe(async () => { (…) t.end(); }); }); (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 56. SOLUTION (MICRO?) SERVICES ARCHITECTURE IN PRACTICE test('Gateway - workflow - create user test', { timeout: 10000 }, async (t: test.Test) => { const mailerClientConfig = config.clients.mailerClient const emailCountBeforeSending = await getCurrentEmailsCount(mailerClientConfig.mailHogUri) const clientSocket = await connectors.getConnector(config.clients.socketIO.type, config.clients.socketIO) const validateMessage = createSocketValidator(clientSocket) const connect$ = fromEvent(clientSocket, 'connect') connect$.subscribe(async () => { (…) t.end(); }); });
  • 58. YOU CAN CONTROL SERVICES DIRECTLY FROM TESTS
  • 59. PASS DOCKER SOCKET TO CONTAINER js: image: our-app:latest build: context: . dockerfile: docker/dev/Dockerfile args: - NPM_TOKEN volumes: &volumes - ./:/dir - /dir/build/ - /var/run/docker.sock:/var/run/docker.sock (MICRO?) SERVICES ARCHITECTURE IN PRACTICE
  • 60. CONTROL CONTAINERS FROM TESTS CONTAINER (MICRO?) SERVICES ARCHITECTURE IN PRACTICE test('Security integration tests - should reconnect to security service', async (t: test.Test) => { pause(config.containers.security.name) const payload = {…} const authenticate = apiGatewayClient.proxy({ payload, method: 'authenticate', service: 'security' }) const timedOut = await Promise.race([waitForTimeout, authenticate]) timedOut === true ? t.pass() : t.fail() await ensureAreUpAndUnpaused([config.containers.security]) t.end() })
  • 61. CONTROL CONTAINERS FROM TESTS CONTAINER (MICRO?) SERVICES ARCHITECTURE IN PRACTICE test('Security integration tests - should reconnect to security service', async (t: test.Test) => { pause(config.containers.security.name) const payload = {…} const authenticate = apiGatewayClient.proxy({ payload, method: 'authenticate', service: 'security' }) const timedOut = await Promise.race([waitForTimeout, authenticate]) timedOut === true ? t.pass() : t.fail() await ensureAreUpAndUnpaused([config.containers.security]) t.end() })
  • 62. CONTROL CONTAINERS FROM TESTS CONTAINER (MICRO?) SERVICES ARCHITECTURE IN PRACTICE export const ensureAreUpAndUnpaused = (containers: ContainerConfig[]): Promise<any> => { const upOrUnpause: Array<Promise<any>> = [] containers.forEach(container => { if (isPaused(container.name)) { upOrUnpause.push(unpause(container)) } else if (isDown(container.name)) { upOrUnpause.push(up(container)) } }) return Promise.all(upOrUnpause) } export const pause = (containerName: string) => shell.exec(`docker pause ${containerName}`)
  • 63. ▸ MONOREPO IS EASIER TO DEVELOP WITH ▸ AUTOMATE PROJECT CREATION ▸ USE CONTRACTS AND GENERATE CODE FROM THEM ▸ USE DOCKER ▸ FOCUS ON INTEGRATION TESTS ▸ TEST NEGATIVE PATHS TL/DR (MICRO?) SERVICES ARCHITECTURE IN PRACTICE