Node.js Middleware
Never again!
Timur Shemsedinov
Chief Technology Architect at Metarhia
Lecturer at Kiev Polytechnic Institute
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
What is middleware?
● Connect contract
(req: Request, res: Response, next: NextFunction)
(req: Request, res: Response)
● Koa contract
async (ctx: Context, next: NextFunction): Promise
async (ctx: Context): Promise
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middlewares and Controller
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res)
(req, res, next)
(req, res, next)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
What is middleware?
● Mixin provocation
● Reference pollution and shared state provocation
● Race condition provocation
● Abstraction leak provocation
● Fat controller and layers mix provocation
● High coupling provocation
● Error ignoring provocation
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
req, res
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Pass ref outer
const sockets = new Map();
app.use((req, res, next) => {
...
sockets.set(userId, req.socket);
...
next();
});
for (const socket of sockets) {
doSomethingWithSocket(socket);
}
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
instance.method(req, res, ...);
instance.method(req.socket, ...);
instance.field = req.headers;
instance.emit('request', req);
const f1 = method.bind(req);
const f2 = method(req)(res);
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const client = new Client(req, res);
abstraction.method(client);
instance.field = client;
client.doSomething(instance);
emitter.emit('connected', client);
func(client).then(...).then(...)...;
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
calls
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
events
calls
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
events
calls
global
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
events
calls
global
fields
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Pass ref everywhere
const ee = new EventEmitter();
app.use((req, res, next) => {
ee.emit('timeout', res);
next();
});
ee.on('timeout', res) => {
setTimeout(() => {
if (!res.writableEnded) res.end('timeout');
}, 5000);
});
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Reference pollution
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Race, data corruption
let userId;
app.use((req, res, next) => {
userId = ...;
next();
});
app.get('/resource', (req, res) => {
if (checkAccess('/resource', userId) === GRANTED) {
res.end('You have an access');
}
});
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Mixin pollutions
Middleware
Middleware
Middleware(req, res, next)
Controller
(req, res, next)
(req, res, next) (req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: mixin to req, res, ctx
app.use((req, res, next) => {
res.groupName = 'idiots';
next();
});
app.get('/user', (req, res) => {
if (res.groupName === 'idiots') {
res.end('Welcome, my dear friend!');
}
});
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: mixin to req, res, ctx
app.use((req, res, next) => {
res.locals.groupName = 'idiots';
next();
});
app.get('/user', (req, res) => {
if (res.locals.groupName === 'idiots') {
res.end('Welcome, my dear friend!');
}
});
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Fat controller
Middleware
Middleware
Middleware Controller
● Access db
● Logging
● Parse headers
● Cache
● Business-logic
● Validation
● Serialization
(req, res, next)
(req, res)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Don’t mix in a single function
● Data access (database connection)
● Business-logic and domain model
● Routing, logging, configuration
● Health and server state reporting
● Working with sockets, headers, cookies, etc.
● Serialization and deserialization
● Templating, caching, cryptography, sessions
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware: Fat controller
router.get('/user/:id', (req, res) => {
if (blacklist.has(res.socket.remoteAddress)) {...}
const id = parseInt(req.params.id);
if (!isValidateUserId(id)) return res.status(500);
const query = 'SELECT * FROM users WHERE id = $1';
pool.query(query, [id], (err, data) => {
if (err) {...}
logger.write(`access user: ${id}`);
res.status(200).json(data.rows);
});
});
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Middleware provocates antipatterns
● Pass-through parameters,
Too many parameters, Data clump
● Accumulate and fire
● Temporary field, State mixins, Shared state,
Global state, State in outer contexts
● Inappropriate intimacy
● High coupling (also see high cohesion pattern)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Layered (onion) architecture
Data Access Layer
Middleware
Middleware
Controller
(req, res, next)
(req, res, next)
(req, res, next)
(req, res)
Service
domain
model
app
servicesuse cases
controllers
infrastructure
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
We need patterns and principles
● DIP (dependency inversion principle)
IoC (Inversion of control)
DI (dependency injection)
● Chain of responsibility (GoF)
● Law of demeter
● SRP (Single responsibility, SOLID)
● Low coupling (GRASP)
const fs = require('fs'); const compose = (...funcs) => x => funcs.
reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab
table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma
=> (row.map((cell, i) => { const width = cellWidth[i]; return i ? c
Keep attention on
● Domain in the middle
● Context isolation
● Layered (onion) architecture
● Don’t depend on frameworks
● Don’t move logic between model and controller
● Always work on abstraction leaking
● Protect data with parallel primitives
github.com/tshemsedinov
youtube.com/TimurShemsedinov
github.com/HowProgrammingWorks/Index
Весь курс по ноде и JS (186 лекций)
https://habr.com/ru/post/485294/
t.me/HowProgrammingWorks
t.me/NodeUA
timur.shemsedinov@gmail.com

Node.js middleware: Never again!

  • 1.
    Node.js Middleware Never again! TimurShemsedinov Chief Technology Architect at Metarhia Lecturer at Kiev Polytechnic Institute
  • 2.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c What is middleware? ● Connect contract (req: Request, res: Response, next: NextFunction) (req: Request, res: Response) ● Koa contract async (ctx: Context, next: NextFunction): Promise async (ctx: Context): Promise
  • 3.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middlewares and Controller Middleware Middleware Middleware(req, res, next) Controller (req, res) (req, res, next) (req, res, next)
  • 4.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c What is middleware? ● Mixin provocation ● Reference pollution and shared state provocation ● Race condition provocation ● Abstraction leak provocation ● Fat controller and layers mix provocation ● High coupling provocation ● Error ignoring provocation
  • 5.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 6.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) req, res
  • 7.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Pass ref outer const sockets = new Map(); app.use((req, res, next) => { ... sockets.set(userId, req.socket); ... next(); }); for (const socket of sockets) { doSomethingWithSocket(socket); }
  • 8.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) instance.method(req, res, ...); instance.method(req.socket, ...); instance.field = req.headers; instance.emit('request', req); const f1 = method.bind(req); const f2 = method(req)(res);
  • 9.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) const client = new Client(req, res); abstraction.method(client); instance.field = client; client.doSomething(instance); emitter.emit('connected', client); func(client).then(...).then(...)...;
  • 10.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) calls
  • 11.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) events calls
  • 12.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) events calls global
  • 13.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res) events calls global fields
  • 14.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 15.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 16.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 17.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 18.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Pass ref everywhere const ee = new EventEmitter(); app.use((req, res, next) => { ee.emit('timeout', res); next(); }); ee.on('timeout', res) => { setTimeout(() => { if (!res.writableEnded) res.end('timeout'); }, 5000); });
  • 19.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Reference pollution Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 20.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Race, data corruption let userId; app.use((req, res, next) => { userId = ...; next(); }); app.get('/resource', (req, res) => { if (checkAccess('/resource', userId) === GRANTED) { res.end('You have an access'); } });
  • 21.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Mixin pollutions Middleware Middleware Middleware(req, res, next) Controller (req, res, next) (req, res, next) (req, res)
  • 22.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: mixin to req, res, ctx app.use((req, res, next) => { res.groupName = 'idiots'; next(); }); app.get('/user', (req, res) => { if (res.groupName === 'idiots') { res.end('Welcome, my dear friend!'); } });
  • 23.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: mixin to req, res, ctx app.use((req, res, next) => { res.locals.groupName = 'idiots'; next(); }); app.get('/user', (req, res) => { if (res.locals.groupName === 'idiots') { res.end('Welcome, my dear friend!'); } });
  • 24.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Fat controller Middleware Middleware Middleware Controller ● Access db ● Logging ● Parse headers ● Cache ● Business-logic ● Validation ● Serialization (req, res, next) (req, res)
  • 25.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Don’t mix in a single function ● Data access (database connection) ● Business-logic and domain model ● Routing, logging, configuration ● Health and server state reporting ● Working with sockets, headers, cookies, etc. ● Serialization and deserialization ● Templating, caching, cryptography, sessions
  • 26.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware: Fat controller router.get('/user/:id', (req, res) => { if (blacklist.has(res.socket.remoteAddress)) {...} const id = parseInt(req.params.id); if (!isValidateUserId(id)) return res.status(500); const query = 'SELECT * FROM users WHERE id = $1'; pool.query(query, [id], (err, data) => { if (err) {...} logger.write(`access user: ${id}`); res.status(200).json(data.rows); }); });
  • 27.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Middleware provocates antipatterns ● Pass-through parameters, Too many parameters, Data clump ● Accumulate and fire ● Temporary field, State mixins, Shared state, Global state, State in outer contexts ● Inappropriate intimacy ● High coupling (also see high cohesion pattern)
  • 28.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Layered (onion) architecture Data Access Layer Middleware Middleware Controller (req, res, next) (req, res, next) (req, res, next) (req, res) Service
  • 29.
  • 30.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c We need patterns and principles ● DIP (dependency inversion principle) IoC (Inversion of control) DI (dependency injection) ● Chain of responsibility (GoF) ● Law of demeter ● SRP (Single responsibility, SOLID) ● Low coupling (GRASP)
  • 31.
    const fs =require('fs'); const compose = (...funcs) => x => funcs. reduce((x, fn) => fn(x), x); const DENSITY_COL = 3; const renderTab table => { const cellWidth = [18, 10, 8, 8, 18, 6]; return table.ma => (row.map((cell, i) => { const width = cellWidth[i]; return i ? c Keep attention on ● Domain in the middle ● Context isolation ● Layered (onion) architecture ● Don’t depend on frameworks ● Don’t move logic between model and controller ● Always work on abstraction leaking ● Protect data with parallel primitives
  • 32.
    github.com/tshemsedinov youtube.com/TimurShemsedinov github.com/HowProgrammingWorks/Index Весь курс поноде и JS (186 лекций) https://habr.com/ru/post/485294/ t.me/HowProgrammingWorks t.me/NodeUA timur.shemsedinov@gmail.com