software architectures, week 2 - decomposition techniques
TRANSCRIPT
Decomposition• Decomposing the multiplication of arrays to simpler contituents can result to a 30x speedup in real-world
applications*.
• Decomposing a system to smaller and simpler units can render it faster, more robust, more scalable and easier to understand.
• Let the system be a mathematical construct, a software system, or a mechanical one.
• The principles come from Systems, not Software Engineering and can be applied to any man-made system.
(*): Handbook of Robust Low-Rank and Sparse Matrix Decomposition, page: 181
What small entropy means?• We have sufficient information for the system.
• If S = 0, we know absolutely everything for the system (from how many molecules it consists, where every molecule is at every given moment, it's temperature in picoKelvin, etc.).
• If S >>> 0 we know nothing for the system.
What it practically means for us?• To successfully decompose a software system, we need to learn as much as
possible about it.
• Internal structure (employed architecture, layers, components, classes, modules, etc.).
• Communication interfaces (between the layers, the components and between the system and ist environment).
• Performance characteristis (speed, throughput, space, latency, etc.).
Preparation can make all the difference in a re-architecting effort
Please do your homework before you start.
What about software decomposition?
We can decompose:
• Algorithms• Software systems • An object hierarchy• A set of functional relationships
Algorithm decomposition• The basic analysis tool for structured programming (what you were
doing when you were 16).
• Coming from the prehistoric times (COBOL & Algol-60).
• The main principle is to break down long sequences of spaghetti code to a structured set of subroutines, blocks and loops.
• Its basis is the Böhm-Jacopini theorem.
What did Böhm and Jacopini said in 1966
A class of control-flow graphs cam compute any computable function if it combines subprograms in only three specific ways (else control structures):
• Execute subprograms in a sequential order.• Execute one of two subprograms according to the value of a boolean
expression (selection).• Execute a subprogram until a boolean expression is true (iteration).
Implications Böhm and Jacopini • Software became more sane.
• A massive flame war started between academics on the degree it should be adopted.
• It bootstrapped many other engineering methods which we use until today.
Like Harlan Mill's COBOL Structuring Facility
1. Identify the basic blocks in the basic blocks in the basic blocks in the procedure.2. Assign a unique label to each block's entry path, and label each block's exit paths with the labels of the
entry paths they connect to. Use 0 for return from the procedure and 1 for the procedure's entry label to each block's entry path, and label each block's exit paths with the labels of the entry paths they connect to. Use 0 for return from the procedure and 1 for the procedure's entry label to each block's entry path, and label each block's exit paths with the labels of the entry paths they connect to. Use 0 for return from the procedure and 1 for the procedure's entry path.
3. Break the procedure into its basic blocks.4. For each block that is the destination of only one exit path, reconnect that block to that exit path.5. Declare a new variable in the procedure (called L for reference).6. On each remaining unconnected exit path, add a statement that sets L to the label value on that path.7. Combine the resulting programs into a selection statement that executes the program with the entry path
label indicated by L8. Construct a loop that executes this selection statement as long as L is not 0.9. Construct a sequence that initializes L to 1 and executes the loop.
Take away• Complicated logic can (nearly) always be decomposed to simpler
elements.
• Simplifying control structures is always a good idea.
• Stick to simple constructs, everybody knows how smart you are.
• Read Knuth's 'Art of Computer Programming'.
Why bother with all these old-fashioned stuff?
• Because monoliths contain long, messy snippets of code most of the time.
• Breaking up a monolith to microservices is an excellent opportunity to get rid off technical debt.
• You will get increased complexity and LoCs due to the need for more communication interfaces. Try to offset it with cleaner and leaner business logic.
Software System decomposition (Structured Analysis)
• Invented in computings prehistoric times (1960s), became popular during the bronze age (1980s).
• A tool which analyses the system's concept into system functions and data entities, which reproduce the system's desired functionality.
• Super useful for decomposing complicated monoliths to smaller entities (micro-services) which satisfy the same set of business requirements with the monolith.
• De Marco's "Structured Analysis and System Specification" is one of the best resources on the subject.
Aspects to analyze• Structural units (top-down perspective, from the very generic to the very
specific).
• Code (classes, objects, modules, interface) and data (primitives, collections, types, class members) entities.
• External (system to human users and other systems) and internal (component to component) communication interfaces
• Data flows
Points of criticism• Selecting entities properly (especially for data flow diagrams).
• Knowing in which level of abstraction to stop the analysis.
• Sizable amount of documentation may be needed to explain the diagrams.
• Prone to frequent changes, need to be maintained.
• Difficult to follow for non-techies.
Object-Oriented decomposition• Object-Oriented programming inherently leads to modular code,
encapsulated in classes.
• Object-Oriented decomposition breaks up a system in a set of smaller classes and objects which are modelling a specific entity or part of the problem domain (or otherwise concerns).
• Very handy when composing microservices, since they naturally result from concerns covered by a set of classes representing a specific concern.
Vertical Decomposition• The classes that consist a system that solves a business problem can
be vertically separated by the concerns they are satisfying.
• The level of abstraction of the concerns can depend on the size of the application and technical aspects like:
• complexity of the business logic• performance of specific code sections• service tenancy model • network performance characteristics• hardware specifications and characteristics
Functional decomposition• Another perspective which can be used in place of the Object-Oriented
decomposition in case you are using a functional programming language.
• It can also be used in tandem with the OO-decomposition and offer a better description of the systems functional It can also be used in tandem with the OO-decomposition and offer a better description of the systems functional It can also be used in tandem with the OO-decomposition and offer a better description of the systems functional behavior.
• The main idea is to describe the system by the set of functions as prescribed by the requirements, decompose them in simpler constituents and finally recostruct the wished behavior with function composition.
What is function composition? • The application of one function to the result of another function
to produce another function.
(g f )(c) = #∘
Diagram source: Wikipedia
What is function composition? (cont.)
• Higher order functions as described by Lambda calculus (functions that take functions as parameters).
>>> def twice(function):... return lambda x: function(function(x))
>>> def f(x):... return x + 3
>>> g = twice(f) >>> print g(7) 13
Can we build our system only in terms of functions?
• Yes!
• It has been done for decades with Lisp, Erlang and Haskell based systems.
A monolith can be teared in many ways
• You will need to consider many factors, which will affect the resulting microservice-based system.
• Communication: Asychronous, message based or synchronous, RESTful or RMI-based?
• Data Persistence: Single data depot or polyglot persistence?
• Deployment scenarios: Single service per host or multiple services per host?
• Resource discovery: Client or server side discovery?
Recommender Engine
Application UI
Scale Cube - Axes• X-Axis – horizontal duplication; Scale by cloning: Scale an application
by running clones behind a load balancer.
Application UI
E-Commerce backend
Application UI
E-Commerce backend
E-Commerce backend
Recommender Engine
Application UI
Scale Cube - Axes• Y-Axis – functional decomposition; Scale by splitting into different
things, microservices.
Application UI
Customers Information
Application UI
Recommender Engine
Billing Service
Recommender Engine
Application UI
Scale Cube - Axes• Z-Axis – Data partitioning; Scale by data sharding.
Application UI
E-Commerce backend (A-G)
Application UI
E-Commerce backend (H-K)
E-Commerce backend (L-Z)
Scale Cube – Axes (cont.)• Can we combine the aspects?
• Yes, but with a lot of caution, it is easy to come up with something extremely complicated which will negate all benefits of decomposition.
• Imagine having microservices responsible for different shards of the dataset.
Customers Information (H-Z)
Recommender Engine
Application UI
Combining axes of the Scale Cube• Apply Y-axis and Z-axis, scaling each service independently.
Application UI
Customers Information
(A-G)
Application UI
Recommender Engine
Billing Service
Partner UICustomers
Information (H-Z)
A list of good advices
https://www.cycligent.com/blog/10-practical-tips-for-making-the-transition-to-microservices/
Tip #2: Start with a component that is already not heavily dependent on the rest of the application. The first break should be simple and a
way to ease into the process.
Tip #3: Try to find components of the application that only serve one function. The simpler the service, the easier it will be to maintain.
Tip #4: Create teams around a component of the application before it gets broken off into a separate service. Once in place, they can then work towards segmentation of their component into a microservice.
Tip #5: Do daily scrum meetings discussions, and then have a weekly scrum meeting to get updates on the major aspects of the
development process. Once transitioned into microservices, the weekly meetings can be on an as needed basis.
Tip #6: Automate and document the build, test, and deployment process so that it can scale with the microservices architecture with
ease.
Tip #7: Invest in tools such as Cycligent that will automate or simplify things for the development team, so they can focus on application
development and less about the complexity of microservices.
Tip #8: Create company-wide processes that requires developers to talk to managers about introducing new technologies.
Tip #9: Ensure that there are multiple developers that are trained in any new technologies introduced into the application. If a developer goes on vacation, you will want to make sure someone else can quickly and
effectively fix any issues that may come about.
Tip #10: Carefully evaluate uncommon or more obscure technologies. With the added complexity of a microservices architecture, the last
thing to worry about is whether or not there are any hidden issues as a result of a new technology.