SOLID principles should better be applied when designing business objects, as soon as maintainability and modularity are needed. They bring uncoupling, abstraction and clean design to Object Oriented Programming. Microservices could be seen as SOLID principles applied to the Service Oriented Architecture. In respect to a classical monolith server, thanks to uncoupling and better granularity, you may enhance deployment, ease cooperation between dev teams, introduce Domain Driven Design and/or Event Driven Design, scale horizontally and still incorporate existing code and services.
After a quick and practical review of SOLID principles, we will see how they may apply to SOA, and how Microservices could be defined using the Open Source mORMot framework interface-based services, on Windows or Linux.
4. Microservices - SOLID meets SOA
SOLID meets SOA
• Abstract via interface
• SOLID Principles
• Service Oriented Architecture
• MicroServices
• mORMot to the rescue
5. Microservices - SOLID meets SOA
Abstract via interface
• In Delphi OOP model
– An interface defines a type
that comprises abstract virtual methods
– It is a declaration of functionality
without an implementation of that
functionality
– It defines "what" is available,
not "how" it is made available
6. Microservices - SOLID meets SOA
Abstract via interface
• Declaring an interface
type
ICalculator = interface(IInvokable)
['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
/// add two signed 32 bit integers
function Add(n1,n2: integer): integer;
end;
• Naming convention: ICalculator
• No visibility attribute: all published
• No fields, just methods and properties
• Unique identifier by GUID (Ctrl Shift G)
7. Microservices - SOLID meets SOA
Abstract via interface
Implementing an interface
type
TServiceCalculator = class(TInterfacedObject, ICalculator)
protected
fBulk: string;
public
function Add(n1,n2: integer): integer;
procedure SetBulk(const aValue: string);
end;
function TServiceCalculator.Add(n1, n2: integer): integer;
begin
result := n1+n2;
end;
procedure TServiceCalculator.SetBulk(const aValue: string);
begin
fBulk := aValue;
end;
• ICalculator added to the “inheritance chain”
• TCalculator implements a behavior
8. Microservices - SOLID meets SOA
Abstract via interface
• Using an interface
function MyAdd(a,b: integer): integer;
var Calculator: ICalculator;
begin
Calculator := TServiceCalculator.Create;
result := Calculator.Add(a,b);
end;
• Strong typing
• The variable defines a behavior (contract)
• Path to abstraction
9. Microservices - SOLID meets SOA
Abstract via interface
• Using an interface
function MyAdd(a,b: integer): integer;
var Calculator: ICalculator;
begin
Calculator := nil;
try
Calculator := TServiceCalculator.Create;
result := Calculator.Add(a,b);
finally
Calculator := nil;
end;
end;
• The compiler generates some hidden code
with automatic initalization/finalization
10. Microservices - SOLID meets SOA
Abstract via interface
• Using an interface
function MyAdd(a,b: integer): integer;
var Calculator: ICalculator;
begin
Calculator := nil;
try
Calculator := TServiceCalculator.Create; // inc(RefCount)
result := Calculator.Add(a,b);
finally
Calculator := nil; // dec(RefCount); =0? -> Free
end;
end;
• The compiler generates some hidden code
with automatic reference counting (ARC)
11. Microservices - SOLID meets SOA
Abstract via interface
There is more than one way to do it
type
TServiceCalculator = class(TInterfacedObject, ICalculator)
protected
fBulk: string;
public
function Add(n1,n2: integer): integer;
procedure SetBulk(const aValue: string);
end;
function TServiceCalculator.Add(n1, n2: integer): integer;
begin
result := n1+n2;
end;
procedure TServiceCalculator.SetBulk(const aValue: string);
begin
fBulk := aValue;
end;
• ICalculator does not enforce
a single implementation class
12. Microservices - SOLID meets SOA
Abstract via interface
There is more than one way to do it
type
TAnotherServiceCalculator = class(TInterfacedObject, ICalculator)
protected
fBulk: string;
public
function Add(n1,n2: integer): integer;
procedure SetBulk(const aValue: string);
end;
function TAnotherServiceCalculator.Add(n1, n2: integer): integer;
begin
result := n2+n1; // I’m a math genius!
end;
procedure TAnotherServiceCalculator.SetBulk(const aValue: string);
begin
fBulk := aValue;
end;
• ICalculator does not enforce
a single implementation class
13. Microservices - SOLID meets SOA
Abstract via interface
• There is more than one way to do it
procedure ResolveCalculator(out: ICalculator);
…
function MyAdd(a,b: integer): integer;
var Calculator: ICalculator;
begin
ResolveCalculator(Calculator);
result := Calculator.Add(a,b);
end;
• The variable defines a behavior (contract)
• Resolve and inject the actual implementation
• Path to abstraction
we don’t know (and don’t need) which class is actually used
14. Microservices - SOLID meets SOA
Abstract via interface
• Abstraction is the key
– Focus on contracts, not completion
– Favor logic over implementation
– Late binding/resolution
– Local/remote execution (SOA)
– Stub/mock tooling
– Dependency Injection / Inversion Of Concern
15. Microservices - SOLID meets SOA
SOLID Principles
• Single responsibility principle
• Open/closed principle
• Liskov substitution principle (design by contract)
• Interface segregation principle
• Dependency inversion principle
16. Microservices - SOLID meets SOA
SOLID Principles
• Single responsibility
– Object should have only a single responsibility
• Open/closed
– Entities should be open for extension,
but closed for modification
• Liskov substitution (design by contract)
– Objects should be replaceable with instances of their
subtypes without altering the correctness of that program
• Interface segregation
– Many specific interfaces are better than one
• Dependency inversion
– Depend upon abstractions, not depend upon concretions
17. Microservices - SOLID meets SOA
SOLID Principles
• Single Responsibility
– When you define a class, it shall be
designed to implement only one feature
– The so-called feature can be seen as
an "axis of change"
or a "a reason for change"
18. Microservices - SOLID meets SOA
SOLID Principles
• Single Responsibility
– One class shall have only one reason
that justifies changing its implementation
– Classes shall have few dependencies
on other classes
– Classes shall be abstracted
from the particular layer they are running
19. Microservices - SOLID meets SOA
• Single Responsibility
– Splitting classes
• Imagine a TBarCodeScanner class
to handle a serial bar-code scanner
• Later on, we want to implement USB support
– First idea may be to inherit
SOLID Principles
20. Microservices - SOLID meets SOA
SOLID Principles
• Single Responsibility
– Splitting classes
• Later on, the manufacturer updates its protocol
To add new features, e.g. 3D scanning or coffee making
– How do we implement this?
– We have two “axis of change”
so we would define two classes
21. Microservices - SOLID meets SOA
SOLID Principles
• Single Responsibility
– Splitting classes
• Later on, the manufacturer updates its protocol
To add new features, e.g. 3D scanning or coffee making
– We defined two classes, which will be joined/composed
in actual barcode scanner classes
23. Microservices - SOLID meets SOA
SOLID Principles
• Single Responsibility
– Do not mix GUI and logic
– Do not mix logic and database
– Do not couple your code to an OS
• Check your uses statement
– There should be no reference to the UI
(e.g. Dialogs) in your business class
– No dependency to DB libraries (this is a hard one)
– No reference to WinAPI.Windows
24. Microservices - SOLID meets SOA
SOLID Principles
• Single Responsibility
– Code smells
• When abusing of
FreeAndNil();
{$ifdef} … {$endif}
uses unit1, unit2, unit3, … unit1000;
• When you can’t find the right method in a class
(mORMot may need some refactoring here)
• When unitary tests are hard to write
25. Microservices - SOLID meets SOA
SOLID Principles
• Open / Close principle
– When you define a class or an interface
• it shall be open for extension
• but closed for modification
26. Microservices - SOLID meets SOA
SOLID Principles
• Open / Closed principle
– Open for extension
• Abstract class is overridden by implementations
• No singleton nor global variable – ever
• Rely on abstraction
• if aObject is aClass then … it stinks!
– Closed for modification
• e.g. via explicitly protected or private members
• RTTI is dangerous: it may open a closed door
27. Microservices - SOLID meets SOA
SOLID Principles
• Open / Closed principle
– In practice
• Define meaningful interface types
– Following the Design By Contract principle
• Define Value objects (DTOs) to transfer the data
– With almost no methods, but for initialization
– With public members, to access the value
• Define Process objects to modify the data
– With public methods, abstracted in the interface
– With mostly private members
28. Microservices - SOLID meets SOA
SOLID Principles
• Open / Close principle
type
TAbstractBarcodeScanner = class(TComponent)
protected
fProtocol: TAbstractBarcodeProtocol;
fConnection: AbstractBarcodeConnection;
public
property Protocol: TAbstractBarcodeProtocol read fProtocol;
property Connection: AbstractBarcodeConnection read fConnection;
...
• Protocol and Connection are read/only
– They are injected by the overridden class constructor
– So it will be Open for extension
29. Microservices - SOLID meets SOA
SOLID Principles
• Open / Close principle
type
TAbstractBarcodeScanner = class(TComponent)
protected
fProtocol: TAbstractBarcodeProtocol;
fConnection: AbstractBarcodeConnection;
public
property Protocol: TAbstractBarcodeProtocol read fProtocol;
property Connection: AbstractBarcodeConnection read fConnection;
...
• Protocol and Connection are read/only
– You could not change the behavior of a class
– It is Closed for modification
32. Microservices - SOLID meets SOA
SOLID Principles
• Liskov substitution principle
– If TChild is a subtype of TParent
• then objects of type TParent
may be replaced
with objects of type TChild
• without altering
any of the desirable properties
of that program
(correctness, task performed, etc.)
33. Microservices - SOLID meets SOA
SOLID Principles
• Liskov substitution principle
– If TChild is a subtype of TParent
• You could be able
to use any inherited or parent class
– Tied to the Open / Closed principle
• So when you inherit,
you keep your contract
you should not break any parent expectation
34. Microservices - SOLID meets SOA
SOLID Principles
• Liskov substitution principle
– Tied to the Open / Closed principle
• If you define a child,
you should not modify the parent
– Code-reusability of the parent implementation
– You will be able
to stub or mock an interface or a class
• Allow correct testing of the whole system:
even if all single unit/integration tests did pass,
real system may not work if this principle was broken
35. Microservices - SOLID meets SOA
SOLID Principles
• Liskov substitution principle
– Design by contract
• Meyer's (Eiffel language) rule:
“when redefining a routine [in a derivative],
you may only replace its precondition by a weaker
one, and its postcondition by a stronger one”
• Define assertions at method level:
– What does it expect, guarantee, and maintain?
– About input/output values or types, side effects,
invariants, errors/exceptions, performance…
– Write comments and unit tests
36. Microservices - SOLID meets SOA
SOLID Principles
• Liskov substitution principle
– Practical, not dogmatic LSP
• “Parent” and “Child” are not absolute
Depending on the context, you may define
a specific level as the “highest” LSP abstract class
» e.g. if you work on server side, you may need
some server-only properties and methods
» LSP idea is to be consistent about your contract,
once the abstraction level is defined
37. Microservices - SOLID meets SOA
SOLID Principles
• Liskov substitution broken code
if aObject is aClass then …
case aObject.EnumeratedType of …
function … abstract;
without further override;
unit parent;
uses child1,child2,child3;
38. Microservices - SOLID meets SOA
SOLID Principles
• Interface segregation principle
– Once an interface has become too 'fat'
it shall be split into smaller
and more specific interfaces
so that any clients of the interface
will only know about the methods
that pertain to them
– In a nutshell, no client should be forced
to depend on methods it does not use
39. Microservices - SOLID meets SOA
SOLID Principles
• Interface segregation principle
– Perfectly fits the SOA uncoupling pattern
• Stateless Microservices for horizontal scaling
– Allows to release memory and resources ASAP
• Smaller objects have more bounded dependencies
– Ease unit testing
• Less coverage
• Less coupling
40. Microservices - SOLID meets SOA
SOLID Principles
• Interface segregation principle
– Excludes RAD
• Logic implemented in TForm TDataModule
where methods are procedural code in disguise
• Put your logic behind interfaces
and call them from your UI (over VCL and FMX)
– Favors DDD
• Segregation avoid domain leaking
• Dedicated interfaces, e.g. for third party
consumption
41. Microservices - SOLID meets SOA
SOLID Principles
• Dependency Inversion
– High-level modules
should not depend on low-level modules
• Both should depend on abstractions
– Following other SOLI(D) principles
– Abstractions should not depend upon details
• Details should depend upon abstractions
– Business Logic should not depend on database
– Application Layer should not depend
on client technology
42. Microservices - SOLID meets SOA
SOLID Principles
• Dependency Inversion
– In most conventional programming style:
• You write low-level components (e.g. DB tables)
• Then you integrate them with high-level
components
– But this limits the re-use of high-level code
• In fact, it breaks the Liskov substitution principle
• It reduces the testing abilities (e.g. need of a real DB)
43. Microservices - SOLID meets SOA
SOLID Principles
• Dependency Inversion may be implemented
– Via a plug-in system
• e.g. external libraries (for embedded applications)
– Using a service locator
• e.g. SOA catalog (SaaS/cloud)
• class resolution from an interface type
– Switch to Event Driven Design
• e.g. using messages over a bus
or Peer-To-Peer communication
44. Microservices - SOLID meets SOA
Service Oriented Architecture
Definition:
A flexible set of design principles
used during the phases of
systems development and integration
To package functionality
as a suite of inter-operable services
that can be used
within multiple, separate systems
from several business domains
45. Microservices - SOLID meets SOA
Service Oriented Architecture
– The SOA implementations rely
on a mesh of uncoupled software services
– Software Service:
• A consumer asks a producer
to act in order to produce a result
• Invocation is (often) free from previous invocation
(stateless), to minimize resource consumption
46. Microservices - SOLID meets SOA
Service Oriented Architecture
– The SOA implementations rely
on a mesh of uncoupled software services
– Those Services comprise
• unassociated, loosely coupled
units of functionality
(each service implements one action)
• that have no direct call to each other
(via protocols, catalogs over a bus)
47. Microservices - SOLID meets SOA
Service Oriented Architecture
– The SOA implementations rely
on a mesh of uncoupled software services
Consumers Service Bus Publishers
Client A Publisher 1
Publisher 2Client B
Publisher 3Client C
Service 1
Service 2
Service 3
48. Microservices - SOLID meets SOA
Service Oriented Architecture
– The SOA implementations rely
on a mesh of uncoupled software services
– Service composition
Consumers Application Service Bus Application Publishers
Business Service Bus Business Publishers
Client A
Composition
Publisher
Composition
Service
Publisher 1
Publisher 2
Publisher 3
Service 1
Service 2
Service 3
49. Microservices - SOLID meets SOA
Service Oriented Architecture
• Expects a top/down implementation
– From n-Tier vertical Architecture
• DB as Root
• Business over DB
– to Clean “onion” Architecture
• Domain as Core
• Persistence as a service
53. Microservices - SOLID meets SOA
Microservices
– SOLID principles meet SOA
• Single responsibility principle
• Open/closed principle
• Liskov substitution principle (design by contract)
• Interface segregation principle
• Dependency inversion principle
54. Microservices - SOLID meets SOA
Microservices
– SOLID principles meet SOA
• Single responsibility principle
Services should have a single axis of change
• Open/closed principle
Services should be extendable, but not modifiable
• Liskov substitution principle (design by contract)
Services should be replaceable
• Interface segregation principle
Several smaller dedicated services
• Dependency inversion principle
Services focus on abstraction, not concretion
55. Microservices - SOLID meets SOA
Microservices
– SOLID principles meet SOA
• Single responsibility principle
Microservices should have a single axis of change
• Open/closed principle
Microservices should be extendable, not modifiable
• Liskov substitution principle (design by contract)
Microservices should be replaceable
• Interface segregation principle
Several smaller dedicated Microservices
• Dependency inversion principle
Microservices focus on abstraction, not concretion
56. Microservices - SOLID meets SOA
Microservices
– SOLID principles meeting SOA
– Favor stateless calls, and/or event-driven
– Implemented as stand-alone uncoupled nodes
with their own persistence
– Enhance scaling abilities
– In-house Microservices focus on domain
On-the-shelf SaaS can be used otherwise
– Expects services discovery and/or balancing
57. Microservices - SOLID meets SOA
Microservices
• Most difficult is to define its own storage
– We were used and told to start from the DB
First task was to define SQL tables
– SOLID expect uncoupled design-by-contract
So each Microservice should have its own DB
– Depending on the use-case, DB may be:
• Classic Client-Server SQL
• Local SQLite3 SQL or Object DB
• NoSQL Client-Server store
• Pure in-memory cache with no persistence
59. Microservices - SOLID meets SOA
Microservices
• Each Microservice owns its persistence
– Resulting in global Eventual Consistency
may be still atomic (SQL) within the Microservice
66. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
– Define your services contracts as interfaces
type
ICalculator = interface(IInvokable)
['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
/// add two signed 32 bit integers
function Add(n1,n2: integer): integer;
end;
• ICalculator interface defines the contract
• Add() method defines the operation
– Handle any kind of parameters
• Including classes, variants, dynamic arrays or records
67. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Define your services contracts as interfaces
• Following SOLID principles
small dedicated interfaces
consumed as abstract method calls
with meaningful naming and types
not polluted by the framework
favoring explicit DTO defined as code
• Run/test/debug/validate implementation locally
68. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Implement the contract on server side
type
TServiceCalculator = class(TInterfacedObject, ICalculator)
public
function Add(n1,n2: integer): integer;
end;
function TServiceCalculator.Add(n1, n2: integer): integer;
begin
result := n1+n2;
end;
… and that’s all !
69. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Host and publish the service
Server.ServiceRegister(
TServiceCalculator,[TypeInfo(ICalculator)],sicShared);
will register the TServiceCalculator class
to implement the ICalculator service
with a single shared instance life time
to any TSQLRestServer instance
70. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Host and publish the service
Server.ServiceDefine(
TServiceCalculator,[ICalculator],sicShared);
will register the TServiceCalculator class
to implement the ICalculator service
with a single shared instance life time
to any TSQLRestServer instance
71. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
– Host and publish the service
72. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Access the service from client side
Client.ServiceDefine([ICalculator],sicShared);
var I: ICalculator;
begin
if Client.Services['Calculator'].Get(I) then
result := I.Add(10,20);
end;
• Register to any kind of TSQLRestClient
• Execution will take place on the server side, using a “fake” class
implementing ICalculator
• Data is transmitted by representation as JSON (array of) values
• Any server-side exception will be transmitted to the client
73. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Access the service from client side
if Client.Services['Calculator'].Get(I) then
POST https://servername/restroot/calculator/add
with { “n1”: 10, “n2”: 20 } as JSON body
returns HTTP/1.1 200
{ “result”: 30 }
result := I.Add(10,20);
• Data is transmitted by representation as JSON (array of) values
• Input parameters as request body, output parameters in answer
74. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
Access the service from client side
if Client.Services['Calculator'].Get(I) then
POST https://servername/restroot/calculator/add
with [10,20] as JSON body between Delphi clients
returns HTTP/1.1 200
{ “result”: 30 }
result := I.Add(10,20);
• Data is transmitted by representation as JSON (array of) values
• Input parameters as request body, output parameters in answer
75. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
– Define your services contracts as interfaces
– By changing one code line, run them
• In-process - direct call in the same thread
• Locally - using local loopback socket
• Remotely - using HTTP/WebSockets connection
76. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
– Define your services contracts as interfaces
– Test-Driven approach
• Abstract from actual infrastructure (DB, network…)
• Stub/mock dependencies for unit testing
• Single process hosting for integrative system tests
77. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
– Integrated features
• Convention-Over-Configuration REST / JSON
from the ground up, with huge performance
• Security, authentication, sessions, threads,
low-level (text) logs + high-level (SQlite3) logs
• WebSockets for EDD realtime interface callbacks
• Rich data model, from strong types to TDocVariant
78. Microservices - SOLID meets SOA
mORMot to the rescue
• ORM/ODM for the persistence layer
– Persistence agnosticism for MicroServices
• ORM over local SQlite3 store
• ORM over remote SQL databases
• ODM over TObjectList
• ODM over MongoDB
Switching from one to another
in a few lines of code, or through settings
79. Microservices - SOLID meets SOA
mORMot to the rescue
• ORM/ODM for the persistence layer
– Persistence agnosticism for MicroServices
• Aggregates mapped to TSQLRecord
to write Repository services
• Advanced ORM features
– REST automated routing, optional remote proxying
– Direct DB-to-JSON serialization (no TDataSet)
– Built-in statement and JSON cache
– Batch (Unit-Of-Work) writes with bulk SQL/NoSQL insertion
– SQL logging/timing, Real-time replication, data sharding
80. Microservices - SOLID meets SOA
mORMot to the rescue
• mORMot distinctive features
• REST and JSON from the ground up
• Open Source 90 man-year development
• From Delphi 6 to latest 10.2
FPC ready (for Linux/BSD server code)
• Not RAD – expressive source code
• Fully integrated SOA + ORM + MVC + …
81. Microservices - SOLID meets SOA
mORMot to the rescue
• Interface-based services
– To be continued, including more code ;)
with next session
Microservices and Event Driven Systems
82. Microservices - SOLID meets SOA
Microservices – SOLID meets SOA
• Credits
some pics taken from
(even if Delphi stand-alone exe
and whole mORMot design
voids the need of containers)
Free download
https://goo.gl/VMbkYm
code and samples at
https://synopse.info