@aahoogendoorn
GROWING A MICROSERVICES LANDSCAPE (WITH SMART USE CASES)Sander HoogendoornIndependent dad, mentor, trainer, software architect, developerPrincipal technology officer, global agile thoughtleader Capgemini
@aahoogendoorn
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
2Sander HoogendoornMe independent..
Dad
Mentor, trainer, software architect, programmer
Books, articles, conferences
Work
Principal technology officer Capgemini
Global design authority agile Capgemini
Chief technology officer insurance company
Web
www.sanderhoogendoorn.com
www.smartusecase.com
www.speedbird9.com
@aahoogendoorn
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
4On being a developer...
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
5Agenda Monoliths
Microservices? Past the hype?
Promises
But…
From monoliths to a micro-landscape
Micro-applications
Components and microservices
Requirements in a micro-landscape
Smart use cases
Agile
Recommendations & considerations
@aahoogendoorn
MONOLITHS Hard to deliver, even harder to test and impossible to maintain
@aahoogendoorn
THE DEPENDENCIES WILL KILL YOU
@aahoogendoorn
MICROSERVICESBeyond the hype?
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
10Microservices. Beyond the hype?
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
11Microservices. Beyond the hype?
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
12Gartner hype cycle
@aahoogendoorn
MICROSERVICESThe clear benefits
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
14
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and
communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully
automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming
languages and use different data storage technologies.
Martin Fowler
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
15Microservices. Scalability
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
16Microservices. Polyglot persistence
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
17Microservices. Promises Products not projects
Scalable
Decentralized governance
Replaceable parts
High performance
Technology independent
Polyglot persistence
Easy to build
Easy to test
Easier deployment than monoliths
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
18Microservices. But… What is a microservice exactly?
Or are we building nanoservices? Miniservices?
Micro-requirements
Components or services
How big is a microservice
Who owns the microservices
What technology
How to define messages
How to test microservices
How to coordinate when services run across components
How to build a deployment pipeline
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
19Microservices. Building a deployment pipeline
CodeDeveloper
TestTest
AcceptanceTest
Acceptance Live
CodeDeveloper
TestTest
AcceptanceTest
Acceptance Live
CodeDeveloper
TestTest
AcceptanceTest
Acceptance Live
CodeDeveloper
TestTest
AcceptanceTest
Acceptance Live
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
20Microservices. Pipeline hell?
Codev.2
DeveloperTestv.2
Testv.2
AcceptanceTestv.2
Acceptancev.2
CodeDeveloper
TestTest
AcceptanceTest
Acceptance Live
Testv.2
AcceptanceTestv.2
Acceptancev.2
Livev.2
DeveloperTest
TestAcceptance
TestAcceptance Live
Codev.3
DeveloperTestv.3
Live
Codev.2
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
21Microservices. Pipeline hell?
Codev.2
DeveloperTestv.2
Testv.2
AcceptanceTestv.2
Acceptancev.2
CodeDeveloper
TestTest
AcceptanceTest
Acceptance Live
Testv.2
AcceptanceTestv.2
Acceptancev.2
Livev.2
DeveloperTest
TestAcceptance
TestAcceptance Live
Codev.3
DeveloperTestv.3
Live
Codev.2
@aahoogendoorn
ARE MICROSERVICESA STAIRWAY TO HEAVEN?
@aahoogendoorn
OR A HIGHWAY TO HELL?
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
FOR THE THINGS WE HAVE TO LEARN
BEFORE WE CAN DO THEM,
WE LEARN BY DOING THEMAristotle
@aahoogendoorn
A MICRO-LANDSCAPE SOFTWARE ARCHITECTURE
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
26A major Dutch insurance companyWe have
Most functionality on an expensive mainframe
A wide variety of large systems written in Java that are hard to maintain and to test, and that are very hard to replace
Individual systems that cover large areas of functionality, usually coupled to departments
Aging technology
No mobile strategy, allowing for new business or new services to clients, and intermediaries
We need to
Shorten time-to-market
Lower TCO
Uphold a fully secure systems landscape
@aahoogendoorn
THE MICRO-LANDSCAPEA software architecture for the near future
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
28A micro-landscape architecture
Communication architecture. The glueHow do we define interfaces between apps and component?
How do we arrange messaging?How do we glue together rapidly changing apps and component?
Application architectureEnd user facing
Different users, different fast evolving needs Which technology is the best for which purpose?
Component architectureComponents and services are evolving rapidly
How do we decide which components we need? How do we deal with versioning?
How do we deal with distributed processes and transactions?
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
29The new micro-landscape Client thinks in business processes, so we implement
business processes
Moving to a new systems landscape, consisting of micro-applications and micro-components
Requirements and documentation are modeled rather than written
Each micro-application implements a single business process
Components serve a single purpose and offers services
Each application and component has its own domain model
Applications and components will have an similar internal software architecture to facilitate ease of maintenance and allow for harvesting re-use
Communication using simple open protocols - REST
@aahoogendoorn
APPLICATIONS
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
32Micro-applications Each micro-application serves a single purpose and
mostly target a single audience, either client, intermediaries, in-house or third parties
Each application implements a single business process
Micro-applications are always human facing, they have a graphical user interface
These user interfaces can be responsive web, native device or even desktop (not preferred) depending on the target audience
Current user interfaces are implemented using HTML5, JavaScript (client-side), Bootstrap (responsive UI), and JSF (server-side)
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
Presentation
Process
Domain
Services
Outside world
PagesGrids / Panels
Controls
Use casesFlow
Domain objectsFactories / Repositories
Enums / Value objects / Tupels / Reference objects
Service gatewaysService clients
Info objects / Search objects
ComponentsRelations Dossiers Intermediaries Accounts Rates
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
35Architecture in code
@aahoogendoorn
COMPONENTS
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
37Components Components are the workers of our software
architecture
Components are fine-grained and targeted at servicing a single business purpose, such as Accounts, Relations or Rates
Components follow the Single Responsibility Principle (SRP).
Over time, components will more and more implement a micro-services architecture
Components will evolve over time
Each component exposes a set of services to the landscape
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
Service interface
Process
Domain
Services
Outside world
Service interfaces
Use casesFlow
Domain objectsFactories / Repositories
Enums / Value objects / Reference objects
Service gateways / Table gatewaysService clients / Connectors
Info objects / Search objects
ComponentsDatabasesRelations Dossiers Rates DB2 MongoDB
@aahoogendoorn
COMMUNICATION
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
41Communication Communication between applications and
components or between components and components needs to be easy to develop, secure and fast
Communication will leverage simple, scalable and safe web protocols, such as REST
Structures from the component’s domain model can be passed between parts in the landscape, either through info objects (DTO’s) or JSON
Structures offered from producers do not necessarily have the same structure required by the consumer
Mapping or wrapping is inevitable
Be aware that neither REST nor JSON is really fully standardized
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
42Standardizing communication
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
43Testing communication
@aahoogendoorn
MODEL FIRST DEVELOPMENTRequirements in the micro-landscape
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
DOING BIG UP-FRONT DESIGN IS DUMB
DOING NO DESIGN IS EVEN DUMBERDave Thomas
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
46Model first
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
47Requirements
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
48Processes, processes and processes
Step 1 Step 2 Step 3 Step 4 Step 5 Step 6
Step 3.1 Step 3.2 Step 3.3 Step 3.4
Step 3.3.1 Step 3.3.2 Step 3.3.3 Step 3.3.4 Step 3.3.5
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
49Agile requirements. Approach
D
E
F
SmartUse Cases
A
Project
Scope
D
Use Cases
A
B C
Hierarchical
Processes
B
D
D
Hierarchical
Processes
Chronological
Processes
Cloud Level Kite Level Sea Level Fish Level
C
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
50Identifying scope at cloud level
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
51Cloud level
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
52Kite level
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
53Modeling smart use cases. Sea and fish level
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
54Different levels of use casesTraditional
use cases
Smart
use cases
Format Textual Visual
Granularity Different Unified
Estimate Hard Easy
Unit of work Lousy Good
Reuse Incidental Normal
Traceability Possible Normal
Testability Poor Good
@aahoogendoorn
MODELING APPS
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
56App smart use case model
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
57Smart use cases in code
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
58App domain modelclass Domeinmodel Aanv raag
«domain object»Domein Objecten::Aanvraag
+ AanvraagId :AanvraagId+ Aanvraagstatus :Aanvraagstatus = Nieuw+ Opslagdatum :LocalDate+ Dossiernummer :Dossiernummer+ Communicatie :Communicatie = Standaardregeling+ Betaalwijze :Betaalwijze = Overschrijving+ Betalingstermijn :Betalingstermijn = Maand+ IBAN :IBAN+ Naam rekeninghouder :String+ Betaalregeling :Betaalregeling = Standaardregeling+ Verzekeringnemers betalen premie :boolean+ Splitsen premie :boolean+ Distributiekanaal :Distributiekanaal
+ getRelatiesBijAanvraag() :DomainList<Relatie>+ getORV() :void+ isRechtstreeksBetalen() :void
«business rule»+ KvK of BSN verzekeringnemer verplicht() :void
«domain object»Domein Objecten::Product
+ Naam :String+ Identificatie :Productidentificatie+ Ingangsdatum :LocalDate+ Type :Producttype = ORV premie A
«domain object»Domein Objecten::Verzekeringnemer
+ Premievrijstelling :boolean+ Beroep :String [0..1]+ Type :TypeVerzekeringnemer
«business rule»+ Woonadres of postbusadres verplicht() :void
«domain object»Domein Objecten::Toetsing
- Geslaagd :boolean- Meldingen :List<String>
«domain object»Domein Objecten::Premiebetaler
«domain object»Domein Objecten::Pandnemer
+ Volgnummer :int+ Verpandingsacte :boolean+ Verpandingsactedatum :LocalDate
«domain object»Domein Objecten::Relatie
+ Achternaam :String [0..1]+ Bedrijfsnaam :String [0..1]+ Burgerservicenummer :Burgerservicenummer [0..1]+ Email :Email [0..1]+ Geboortedatum :LocalDate [0..1]+ Geldig tot :LocalDate [0..1]+ Geldig vanaf :LocalDate+ Geslacht :Geslacht [0..1]+ Klantnummer :Relatienummer+ KvK nummer :KvKNummer [0..1]+ Loonheffingskorting :Boolean [0..1]+ Mobiel :Telefoonnummer [0..1]+ Overlijdensdatum :LocalDate [0..1]+ RDW nummer :RDW Nummer [0..1]+ Rechtsvorm :Rechtsvorm [0..1]+ Toestand :Relatietoestand+ Type :Relatietype+ Vast :Telefoonnummer+ Voorletters :String [0..1]+ Voorvoegsel :String [0..1]
«domain object»Domein Objecten::Adres
+ Adrestype :Adrestype+ Huisnummer :Huisnummer [0..1]+ Huisnummertoevoeging :String+ Land :Land [0..1]+ Woonplaats :String [0..1]+ Postcode :Postcode [0..1]+ Straat :String [0..1]
«domain object»Domein Objecten::ORV
+ Distributiekosten :Bedrag+ Totale kosten :Bedrag+ Verpandingsvoornemen :boolean+ Standaardbegunstiging :boolean = true
+ getRelatiesBijProduct() :DomainList<Relatie>+ getTotaleKosten() :void
«business rule»+ pandnemer verplicht bij verpandingsvoornemen() :void
«domain object»Domein Objecten::Begunstiging
+ Hoedanigheid :Hoedanigheid+ Volgnummer :int+ Begunstigde :String
«domain object»Domein Objecten::Assurantieadviseur
+ AANummer :AANummer+ Naam :String+ Voorvoegsel :String [0..1]+ Achtervoegsel :String [0..1]+ Telefoonnummer :Telefoonnummer [0..1]+ EmailAdres :Email+ Status :AA Status+ Rayonnummer :Rayonnummer+ Verplichtmaatchappijincasso :boolean+ Schadeuitkering :Schadeuitkering+ HeeftVergunningSchadeZakelijk :boolean+ HeeftVergunningSchadeParticulier :boolean+ HeeftVergunningSchadeInkomen :boolean+ HeeftVergunningSchadeVermogen :boolean+ Vergunningplicht :boolean+ VergunningVermogen :boolean
+ getVestiging() :void+ vindAdres(Adrestype) :void+ getVolledigeNaam() :void+ getPostbus() :void
«domain object»Domein Objecten::Dossier
+ Dossiernummer :Dossiernummer
«domain object»Domein Objecten::Portefeuille
+ Portefeuillenummer :Portefeuillenummer+ Rekeningnummer :IBAN+ NRC rekeningnummer :NRC Rekeningnummer+ Incassomethode bij mutatie :InExcassotraject+ Incassomethode bij prolongatie :InExcassotraject
«domain object»Domein Objecten::Notitie
+ Titel :String+ Tekst :String
«domain object»Domein Objecten::Vraag
+ Identificatie :Vraagidentificatie+ Vraagtype :Vraagtype+ Vraagtekst :String+ Antwoordtype :Antwoordtype+ Volgorde :Rangnummer
+ isToelichtingVerplicht() :boolean
«domain object»Domein Objecten::Clausule
+ Identificatie :ClausuleIdentificatie+ Naamgeving :Naamgeving+ Tekst :String+ Optionaliteit :Optionaliteit+ Ingangsdatum :LocalDate+ Einddatum :LocalDate
Volgnummer hier betekent
rangnummer in de component
Overeenkomst.
«domain object»Domein Objecten::Antwoord
+ Code :String+ Antwoordvolgorde :Rangnummer+ Antwoordtekst :String+ Waarde :String+ ToelichtingVerplicht :boolean+ GegevenAntwoord :String+ GegevenToelichting :String
«domain object»Domein Objecten::Voorwaarden
+ Code :String+ NaamIntern :String+ NaamExternKort :String+ NaamExternLang :String+ Optionaliteit :Optionaliteit+ Url :URL
1 1..2
1*
1
*
1
*
1
+verpanding0..*
11..*
0..1
1
1
1
1
1
11..*
1
1..2
* 1
1
0..1
1
1
0..1
0..1
1
1
+postbusadres 1
1
+woonadres 1
1 1..*
1
+productClausules
*
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
Presentation
Process
Domain
Services
Outside world
PagesGrids / Panels
Controls
Use casesFlow
Domain objectsFactories / Repositories
Enums / Value objects / Tupels / Reference objects
Service gatewaysService clients
Info objects / Search objects
ComponentsRelations Dossiers Intermediaries Accounts Rates
@aahoogendoorn
MODELING COMPONENTS
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
61Component smart use case model
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
62Component smart use case modelclass Aanv raag
«service interface»OphalenAanvraagService
«get»+ Ophalen(AanvraagId) :void
«service interface»AanvraagService
«post»+ Opslaan(String)
«read»
Ophalen Aanvraag
Aanvragen
«write»
Opslaan Aanvraag
«write»
Deleten Aanvraag
«service interface»DeleteService
+ Delete(AanvraagId) :void
class Begunstiging
«service interface»BegunstigingService
«get»+ BepalenBegunstiging(Productidentificatie) :BegunstigingInfo[] A
(from Processen)
«read»
Bepalen Begunstiging
Aanvragen
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
63Component domain model
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
64Business rules on the domain model
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
Service interface
Process
Domain
Services
Outside world
Service interfaces
Use casesFlow
Domain objectsFactories / Repositories
Enums / Value objects / Reference objects
Service gateways / Table gatewaysService clients / Connectors
Info objects / Search objects
ComponentsDatabasesRelations Dossiers Rates DB2 MongoDB
@aahoogendoorn
MODEL FIRST DEVELOPMENTBringing it all together
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
69Smart use cases
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
70Identifying reuse
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
72The smart use case life cycle
Define work
on use case
Write
test cases
Generate
and build
use case
Run
test cases
Adjust
use case
Describe
use case
Accept
use case
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
73Smart use case life cycle in real life
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
74Lifecycle on a board
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
75Definition of Done
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
76Writing smart use cases. Using Enterprise Architect
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
77Analysis & Design. Wire frame with use case
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
78Writing smart use cases. Using Enterprise Architect
Alternative flows
Exception flows
Basic flow
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
79Test Design. Testing smart use cases
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
80Test scenarios for a smart use case
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
81Testing a smart use case. Using Enterprise Architect
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
82Writing smart use cases. Using Enterprise Architect
Fields on form
with smart use cases
Smart use case
specific business rules
and validations
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
83Building code. Twitter Bootstrap & JSF
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
85Shared libraries?
@aahoogendoorn
(SOME) RECOMMANDATIONSAND CONSIDERATIONS
MICROSERVICES USING SMART USE CASES©2014 .Sander Hoogendoorn.. All Rights Reserved@aahoogendoorn
87Minimal viable product
@aahoogendoorn
FIRST DO ITTHEN DO IT RIGHTTHEN DO IT BETTER
@aahoogendoorn
COMMUNICATIONVIA REST IS NOT
AS EASY AS IT PROMISES
@aahoogendoorn
GO WITH THE FLOW…
@aahoogendoorn
ALLOW THE TEAM TO LEARN…
@aahoogendoorn
AND HAVE FUN
@aahoogendoorn
www.sanderhoogendoorn.comwww.smartusecase.comwww.speedbird9.com
@aahoogendoorn
REFERENCES AND QUESTIONS