рдирдорд╕реНрдХрд╛рд░, рд╣реЗрдмреНрд░! рдореИрдВ рдЖрдкрдХреЗ рдзреНрдпрд╛рди рдореЗрдВ рд░реЗрдореЛ рдПрдЪред рдЬреИрдирд╕реЗрди рджреНрд╡рд╛рд░рд╛ рдЯрд╛рдЗрдкреЛрдбрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдФрд░ рдЗрдирд╡рд░реНрд╕реАрдлрд┐рдЬ рдХреЗ рд╕рд╛рде Node.js рдореЗрдВ SOLID рдФрд░ рдкреНрдпрд╛рдЬ рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд▓реЗрдЦ рдХрд╛ рдПрдХ рдзреНрдпрд╛рди рдЖрдХрд░реНрд╖рд┐рдд рдХрд░ рд░рд╣рд╛ рд╣реВрдВ ред
рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рд╣рдо рдкреНрдпрд╛рдЬ рдХреЗ рд░реВрдк рдореЗрдВ рдЬреНрдЮрд╛рдд рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЛ рджреЗрдЦреЗрдВрдЧреЗред рд╕реНрддрд░рд┐рдд рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ - рдЖрд╡реЗрджрди рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдПрдХ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╣реИ рдХрд┐ рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХрд╛ рдкрд╛рд▓рди рдХрд░рддрд╛ рдареЛрд╕ ред рдпрд╣ DDD рдФрд░ рдХрд╛рд░реНрдпрд╛рддреНрдордХ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рдХреЗ рдХреБрдЫ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХреЗ рдкреНрд░рднрд╛рд╡ рдореЗрдВ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдерд╛, рдФрд░ рдирд┐рд░реНрднрд░рддрд╛ рдЗрдВрдЬреЗрдХреНрд╢рди рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрдд рдХреЛ рднреА рд╕рдХреНрд░рд┐рдп рд░реВрдк рд╕реЗ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИред
рдкреГрд╖реНрдарднреВрдорд┐
рдЗрд╕ рдЦрдВрдб рдореЗрдВ рдПрдХ рд╕реНрддрд░рд┐рдд рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЛ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдХреБрдЫ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рд╡рд┐рдХрд╛рд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдФрд░ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред
рдЬрд┐рдореНрдореЗрджрд╛рд░реА рдХреЗ рд╡рд┐рднрд╛рдЬрди рдХрд╛ рд╕рд┐рджреНрдзрд╛рдВрдд
рджреЗрдпрддрд╛ рдПрдХ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рдХреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЗ рд╡рд┐рднрд┐рдиреНрди рдкрд╣рд▓реБрдУрдВ рдХреЛ рд╕рдВрджрд░реНрднрд┐рдд рдХрд░рддреА рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, "рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рддрд░реНрдХ" рдФрд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдЬрд┐рд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд╡рд┐рднрд┐рдиреНрди рдЬрд┐рдореНрдореЗрджрд╛рд░рд┐рдпрд╛рдВ рд╣реИрдВред
рдЬрд┐рдореНрдореЗрджрд╛рд░реА рдХрд╛ рдкреГрдердХреНрдХрд░рдг рдЙрд╕ рдХреЛрдб рдХреЛ рдЕрд▓рдЧ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдЬреЛ рдкреНрд░рддреНрдпреЗрдХ рдЬрд┐рдореНрдореЗрджрд╛рд░реА рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рддрд░реНрдХ рдХреЗ рдХреЛрдб рдХреЛ рдмрджрд▓рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП, рдФрд░ рдЗрд╕реА рддрд░рд╣ред
рдареЛрд╕ рд╕рд┐рджреНрдзрд╛рдВрдд
SOLID рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкрд╛рдБрдЪ рд╕рд┐рджреНрдзрд╛рдВрддреЛрдВ рдХрд╛ рдПрдХ рд╕рдВрдХреНрд╖рд┐рдкреНрдд рд░реВрдк рд╣реИ:

рдПрдХрдорд╛рддреНрд░ рдЬрд┐рдореНрдореЗрджрд╛рд░реА рдХрд╛ рд╕рд┐рджреНрдзрд╛рдВрдд
рдПрдХ рд╡рд░реНрдЧ рдХреА рдПрдХ рд╣реА рдЬрд┐рдореНрдореЗрджрд╛рд░реА рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред (рдЕрдиреБрд╡рд╛рджрдХ рдХрд╛ рдиреЛрдЯ: рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, рдПрдХ рдЕрдзрд┐рдХ рд╕рдЯреАрдХ рд╢рдмреНрдж рд╣реИ: "рдПрдХ рд╡рд░реНрдЧ рдХреЗ рдкрд╛рд╕ рдПрдХ рдФрд░ рдкрд░рд┐рд╡рд░реНрддрди рдХрд╛ рдХреЗрд╡рд▓ рдПрдХ рдХрд╛рд░рдг рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП")
рдХрд┐рд╕реА рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рддреЛрдбрд╝рдиреЗ рдХрд╛ рд╕рдмрд╕реЗ рдкреНрд░рднрд╛рд╡реА рддрд░реАрдХрд╛ рдПрдХ рджрд┐рд╡реНрдп рд╡рд░реНрдЧ рдмрдирд╛рдирд╛ рд╣реИред
рджрд┐рд╡реНрдп рд╡рд░реНрдЧ рдПрдХ рдРрд╕рд╛ рд╡рд░реНрдЧ рд╣реИ рдЬреЛ рдмрд╣реБрдд рдХреБрдЫ рдЬрд╛рдирддрд╛ рдФрд░ рдХрд░рддрд╛ рд╣реИред рдпрд╣ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдПрдХ рд╡рд┐рд░реЛрдзреА рдкреИрдЯрд░реНрди рдХрд╛ рдПрдХ рдЕрдЪреНрдЫрд╛ рдЙрджрд╛рд╣рд░рдг рд╣реИред
. , , , . , , .
TypeScript . email, .
class Person {
public name : string;
public surname : string;
public email : string;
constructor(name : string, surname : string, email : string){
this.surname = surname;
this.name = name;
if(this.validateEmail(email)) {
this.email = email;
}
else {
throw new Error("Invalid email!");
}
}
validateEmail(email : string) {
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
return re.test(email);
}
greet() {
alert("Hi!");
}
}
email Email
:
class Email {
public email : string;
constructor(email : string){
if(this.validateEmail(email)) {
this.email = email;
}
else {
throw new Error("Invalid email!");
}
}
validateEmail(email : string) {
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
return re.test(email);
}
}
class Person {
public name : string;
public surname : string;
public email : Email;
constructor(name : string, surname : string, email : Email){
this.email = email;
this.name = name;
this.surname = surname;
}
greet() {
alert("Hi!");
}
}
, , , /.
/
.
, /:
class Rectangle {
public width: number;
public height: number;
}
class Circle {
public radius: number;
}
function getArea(shapes: (Rectangle|Circle)[]) {
return shapes.reduce(
(previous, current) => {
if (current instanceof Rectangle) {
return current.width * current.height;
} else if (current instanceof Circle) {
return current.radius * current.radius * Math.PI;
} else {
throw new Error("Unknown shape!")
}
},
0
);
}
( ). , . , ( ), , getArea
, , .
, , :
interface Shape {
area(): number;
}
class Rectangle implements Shape {
public width: number;
public height: number;
public area() {
return this.width * this.height;
}
}
class Circle implements Shape {
public radius: number;
public area() {
return this.radius * this.radius * Math.PI;
}
}
function getArea(shapes: Shape[]) {
return shapes.reduce(
(previous, current) => previous + current.area(),
0
);
}
( ) ( ).
.
, , . :
function getArea(shapes: Shape[]) {
return shapes.reduce(
(previous, current) => previous + current.area(),
0
);
}
Shape
, . , getArea
, Shape
. , TypeScript, (, Shape
area
, . , , .
, , , .
.
, : Rectangle
Circle
. , , . Shape
:
interface Shape {
area(): number;
serialize(): string;
}
class Rectangle implements Shape {
public width: number;
public height: number;
public area() {
return this.width * this.height;
}
public serialize() {
return JSON.stringify(this);
}
}
class Circle implements Shape {
public radius: number;
public area() {
return this.radius * this.radius * Math.PI;
}
public serialize() {
return JSON.stringify(this);
}
}
, :
function getArea(shapes: Shape[]) {
return shapes.reduce(
(previous, current) => previous + current.area(),
0
);
}
, , , :
return rectangle.serialize();
, serialize Shape
. -, тАФ . .
, - , , , :
interface RectangleInterface {
width: number;
height: number;
}
interface CircleInterface {
radius: number;
}
interface Shape {
area(): number;
}
interface Serializable {
serialize(): string;
}
, .
class Rectangle implements RectangleInterface, Shape {
public width: number;
public height: number;
public area() {
return this.width * this.height;
}
}
class Circle implements CircleInterface, Shape {
public radius: number;
public area() {
return this.radius * this.radius * Math.PI;
}
}
function getArea(shapes: Shape[]) {
return shapes.reduce(
(previous, current) => previous + current.area(),
0
);
}
, .
class RectangleDTO implements RectangleInterface, Serializable {
public width: number;
public height: number;
public serialize() {
return JSON.stringify(this);
}
}
class CircleDTO implements CircleInterface, Serializable {
public radius: number;
public serialize() {
return JSON.stringify(this);
}
}
, ( ) ( , ).
, RectangleDTO
Rectangle
(DRY). , . , , . , .
DRY, DRY SOLID. , DRY , SOLID " " .
. - .
. , .
, SOLID D. , , SOLID. , SOLID . , , , :
SOLID . , , . , JavaScript ES5 ES6, SOLID . , TypeScript .
-- MVC
: , .

. , . , Product , SQL Server.
, . , , , . , .
. . , , , .
, . MVC, ; . , , .
MVC ( , ) . , . . тАФ . - тАФ . , . , , -.
MVC , , . , , -. MVC тАФ .
-
MVC , - . , . -, . - , . , , -.
- . -, , - . - . - :
. , . , , . .

. , , , , .
-. , , (Data Mapper) .
. , catalog , . , , SQL- Sharepoint (CAML). .
( ):

тАФ . , , , , , .
DDD, "". :
, , - . . ( ) - . , , ( SQL) ( HTTP).
, , , . -. (, ) (, ) , , - .
: (), (). , тАФ HTTP , , . AircraftController
AircraftRepository
:
import { inject } from "inversify";
import { response, controller, httpGet } from "inversify-express-utils";
import * as express from "express";
import { AircraftRepository } from "@domain/interfaces";
import { Aircraft } from "@domain/entities/aircraft";
import { TYPE } from "@domain/types";
@controller("/api/v1/aircraft")
export class AircraftController {
@inject(TYPE.AircraftRepository) private readonly _aircraftRepository: AircraftRepository;
@httpGet("/")
public async get(@response() res: express.Response) {
try {
return await this._aircraftRepository.readAll();
} catch (e) {
res.status(500).send({ error: "Internal server error" });
}
}
}
AircraftController
HTTP AircraftRepository
. AircraftRepository
HTTP. :

. "comp" (composition) , AircraftRepository
AircraftController
. "ref" (reference) , AircraftController
Aircraft
.
AircraftRepository
, , AircraftController
AircraftRepository
, :

, () ( ). , , .

AircraftRepository
. , , - . "" InversifyJS
. InversifyJS
, @inject
, . , :
@inject(TYPE.AircraftRepository) private readonly _aircraftRepository: AircraftRepository;
, InversifyJS
:
container.bind<AircraftRepository>(TYPE.AircraftRepository).to(AircraftRepositoryImpl);
AircratRepository
Repository<T>
, .
import { Aircraft } from "@domain/entities/aircraft";
export interface Repository<T> {
readAll(): Promise<T[]>;
readOneById(id: string): Promise<T>;
}
export interface AircraftRepository extends Repository<Aircraft> {
}
:

Repository<T>
AircraftRepository
:
Repository<T>
Gene- ricRepositoryImpl<D, E>
AircraftRepository
AircraftRepositoryImpl
.
Repository<T>
import { injectable, unmanaged } from "inversify";
import { Repository } from "@domain/interfaces";
import { EntityDataMapper } from "@dal/interfaces";
import { Repository as TypeOrmRepository } from "typeorm";
@injectable()
export class GenericRepositoryImpl<TDomainEntity, TDalEntity> implements Repository<TDomainEntity> {
private readonly _repository: TypeOrmRepository<TDalEntity>;
private readonly _dataMapper: EntityDataMapper<TDomainEntity, TDalEntity>;
public constructor(
@unmanaged() repository: TypeOrmRepository<TDalEntity>,
@unmanaged() dataMapper: EntityDataMapper<TDomainEntity, TDalEntity>
) {
this._repository = repository;
this._dataMapper = dataMapper;
}
public async readAll() {
const entities = await this._repository.readAll();
return entities.map((e) => this._dataMapper.toDomain(e));
}
public async readOneById(id: string) {
const entity = await this._repository.readOne({ id });
return this._dataMapper.toDomain(entity);
}
}
, EntityDataMapper
TypeOrmRepository
. .
, EntityDataMapper
:
export interface EntityDataMapper<Domain, Entity> {
toDomain(entity: Entity): Domain;
toDalEntity(domain: Domain): Entity;
}
EntityDataMapper
:
import { toDateOrNull, toLocalDateOrNull } from "@lib/universal/utils/date_utils";
import { Aircraft } from "@domain/entities/aircraft";
import { AircraftEntity } from "@dal/entities/aircraft";
import { EntityDataMapper } from "@dal/interfaces";
export class AircraftDataMapper implements EntityDataMapper<Aircraft, AircraftEntity> {
public toDomain(entity: AircraftEntity): Aircraft {
}
public toDalEntity(mortgage: Aircraft): AircraftEntity {
}
}
EntityDataMapper
, TypeOrmRepository
. :

, , AircraftRepository
:
import { inject, injectable } from "inversify";
import { Repository as TypeOrmRepository } from "typeorm";
import { AircraftRepository } from "@domain/interfaces";
import { Aircraft } from "@domain/entities/aircraft";
import { GenericRepositoryImpl } from "@dal/generic_repository";
import { AircraftEntity } from "@dal/entities/aircraft";
import { AircraftDataMapper } from "@dal/data_mappers/aircraft";
import { TYPE } from "@dal/types";
@injectable()
export class AircraftRepositoryImpl
extends GenericRepositoryImpl<Aircraft, AircraftEntity>
implements AircraftRepository {
public constructor(
@inject(TYPE.TypeOrmRepositoryOfAircraftEntity) repository: TypeOrmRepository<AircraftEntity>
) {
super(repository, new AircraftDataMapper())
}
}
, , :

: (, ) (, ).
, тАФ тАФ .

рдЗрд╕ рд╡рд╛рд╕реНрддреБ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдиреЗ рдкрд┐рдЫрд▓реЗ рджрд╕ рд╡рд░реНрд╖реЛрдВ рдореЗрдВ рдмрдбрд╝реА рдЙрджреНрдпрдо рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ рдореЗрд░реЗ рд▓рд┐рдП рдХрд╛рдо рдХрд┐рдпрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореИрдВрдиреЗ рд╡рд┐рд╢рд╛рд▓ рд╕реНрддрд░рд┐рдд рдореЛрдиреЛрд▓рд┐рде рдХреЛ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд┐рдпрд╛ рдЬреЛ рдХрд┐ рд╕рдорд╛рди рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХрд╛ рдкрд╛рд▓рди рдХрд░рддрд╛ рд╣реИред рдореБрдЭреЗ рдпрд╣ рдХрд╣рдирд╛ рдкрд╕рдВрдж рд╣реИ рдХрд┐ рд▓реЗрдпрд░реНрдб рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░ рдкрд░ рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕реЗрдЬ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ "рдмрд▓реНрдмреЛрдВ рдХрд╛ рдмреИрдЧ" рд╣реИред
рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдпрд╣ рд▓реЗрдЦ рдкрд╕рдВрдж рдЖрдпрд╛ рд╣реЛрдЧрд╛! рдХреГрдкрдпрд╛ рдЯрд┐рдкреНрдкрдгреА рдореЗрдВ рдпрд╛ рд▓реЗрдЦрдХ @RemoHJansen рдХреЛ рдЕрдкрдиреЗ рд╡рд┐рдЪрд╛рд░ рд╕рд╛рдЭрд╛ рдХрд░реЗрдВ ред
рдЕрдВрдд рддрдХ рдкрдврд╝рдиреЗ рд╡рд╛рд▓реЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдмреЛрдирд╕ рдПрдХ рдХрд╛рдо рдХреЛрдб рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рд╛рде рдПрдХ рднрдВрдбрд╛рд░ рд╣реИ ред