Why services?
A service...[i]s a logical representation of a repeatable business activity that has a specified outcome... Is self-contained[, m]ay be composed of other services [and i]s a "black box" to consumers of the service.
request('users/123'); // -> { name: 'Joe User', id: 123, loggedIn: true }
Deceptively simple.
- repeatable
- self-contained
- composed
- black-boxed
Why modules?
Product systems are deemed “modular” ... when they can be decomposed into a number of components that may be mixed and matched in a variety of configurations. The components are able to connect, interact, or exchange resources ... by adhering to a standardized interface. Unlike a tightly integrated product whereby each component is designed to work specifically (and often exclusively) with other particular components in a tightly coupled system, modular products are systems of components that are “loosely coupled.”
//sayHi.js
export default () => {
shakeHands();
return 'Hi Jared';
};
//sayHi.js
import shakeHands from './shakeHands';
export default () => {
shakeHands();
return 'Hi Jared';
};
//sayHi.js
export default(shakeHands, name) => {
shakeHands();
return `Hi ${name}`;
};
//interact.js
export default verbalAction =>
physicalAction =>
name => {
physicalAction();
return `${verbalAction} ${name}`;
};
//Usage
//interact('Hi')(shakeHands)('Jared'); // Hi Jared (shakes hand)
//interact('Bye)(wave)('Matt'); // Bye matt (waves)
//interact('So\'s your face')(point)('Bruce'); //So's your face Bruce
//interact.js
export default (verbalAction , physicalAction, name) => {
physicalAction();
return `
${verbalAction} ${name}`;
};
};
//Usage
//interact('Hi', shakeHands, 'Jared'); // Hi Jared (shakes hand)
//interact('Bye', wave, 'Matt'); // Bye matt (waves)
//interact('So\'s your face', point, 'Bruce'); //So's your face Bruce
import interact from './interact';
const sayHi = interact('Hi')(wave),
sayBye = interact('Bye')(sadTurn),
face = interact('So\'s your face'),
playFace = face(noop),
retaliFace = face(point);
export default (nameA, nameB) => `
${nameA}: ${sayHi(nameB)}
${nameB}: ${playFace(nameA)}
${nameA}: ${retaliFace(nameB)}
${nameB}: ${sayBye(nameA)}`;
import conversation from './conversation';
conversation('Cory', 'Blake');
// Cory: Hi Blake (waves)
// Blake: So's your face Cory
// Cory: So's your face Blake (points)
// Blake: Bye Cory (sadly turns away)
//sayHi.js
export default (customaryGreeting, name) => {
customaryGreeting();
return `Hi ${name}`;
};
Why components?
[A] reuse-based approach to defining, implementing and composing loosely coupled independent components into systems.
[I]t is often said that components are modular and cohesive.
[C]omponents communicate with each other via interfaces
<picture>
<source media="(min-width: 800px)"
sizes="80vw"
srcset="lighthouse-landscape-640.jpg 640w,
lighthouse-landscape-1280.jpg 1280w,
lighthouse-landscape-2560.jpg 2560w">
<img src="lighthouse-160.jpg" alt="lighthouse"
sizes="80vw"
srcset="lighthouse-160.jpg 160w,
lighthouse-320.jpg 320w,
lighthouse-640.jpg 640w,
lighthouse-1280.jpg 1280w">
</picture>
reusability
-
Repeatable
-
Configurable
-
Extensible
-
Composable
}
Reusable
I think the lack of reusability comes [with] all this implicit environment... You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
testable
angular.factory('config', function () {
return function (options) {
const defaults = {
desert: 'gelato',
entree: 'chicken cordon bleu',
appetizer: 'cesar salad',
drink: 'mint limeade'
}
return angular.extend({}, defaults, options);
}
}
angular.factory('config', function () {
return function (options) {
const defaults = {
desert: 'gelato',
entree: 'chicken cordon bleu',
appetizer: 'cesar salad',
drink: 'mint limeade'
}
return Object.assign({}, defaults, options);
}
}
export default function () {
return function (options) {
const defaults = {
desert: 'gelato',
entree: 'chicken cordon bleu',
appetizer: 'cesar salad',
drink: 'mint limeade'
}
return Object.assign({}, defaults, options);
}
}
//app.js
import config from './config';
angular.module('myApp', [])
.factory('config', config);
consider:
import test from 'tape';
import config from './config';
test('should return config object with defaults for missing properties',
assert => {
assert.deepEqual(config({drink: 'diet dr. pepper'}), {
desert: 'gelato',
entree: 'chicken cordon bleu',
appetizer: 'cesar salad',
drink: 'diet dr. pepper'
});
});
to test:
When your modules have this implicit environment, your modules must be tested within that environment. You are no longer unit testing, your are environment testing.
maintainable
Less code almost always means less (sic) bugs.
You make small focused modules for reusability and to make it possible to build larger more advanced things that are easier to reason about.
//deepPath.js
export default (obj, path) => path.split(/[.\/]/)
.reduce((currObj, nextProp) => Object(currObj)[nextProp], obj);
//deepPath.js
export default (obj, path) => path.split(/[.\/]/)
.reduce((currObj, nextProp) => Object(currObj)[nextProp], obj);
---
import deepPath from './deepPath';
const obj = {
some: {
deep: ['path', 'some', 'where']
}
};
deepPath(obj, 'some.deep.0'); // 'path'
deepPath(obj, 'some/deep/1'); // 'some'
composable
Think of node modules as lego blocks. You don't necessarily care about the details of how it's made. All you need to know is how to use the lego blocks to build your lego castle. By making small focused modules you can easily build large complex systems without having to know every single detail of how everything works. Our short term memory is finite.
identifying composability
f(x) -> g(f') -> h(g')
h( g( f(x) ) )
f(x) -> i(w) -> h(z)
f(x) -> g(f') -> h(g')
h( g( f(x) ) )
-
services
-
modules
-
components
}
composable
Identify the composablity of your unit and you will identify whether you have a proper separation of concerns.
While you're training yourself to think in terms of composable systems, error on the side of too small. You will know quickly when you have crossed the line.
What is holding you back?
- deadlines
- customer doesn't want it
- solution manager doesn't want it
- architect doesn't want it
- build it into you estimate
- why is your customer involved in implementation details?
- solution manager will love the productivity.
- that doesn't prevent you from writing your code for composition.
This is the direction ICS is going.
All that remains is for us to just do it.
Composition
By Cory Brown
Composition
Componintization, Modularization, Micro-services and SOA are all aspects of the same philosophy -- composition.
- 1,370