рдЯрд╛рдЗрдкрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдФрд░ рдЗрдирд╡рд░реНрд╕реАрдлреНрдпреВрдЬ рдХреЗ рд╕рд╛рде Node.js рдореЗрдВ SOLID рдФрд░ рд╕реНрддрд░рд┐рдд рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛

рдирдорд╕реНрдХрд╛рд░, рд╣реЗрдмреНрд░! рдореИрдВ рдЖрдкрдХреЗ рдзреНрдпрд╛рди рдореЗрдВ рд░реЗрдореЛ рдПрдЪред рдЬреИрдирд╕реЗрди рджреНрд╡рд╛рд░рд╛ рдЯрд╛рдЗрдкреЛрдбрд╕реНрдХреНрд░рд┐рдкреНрдЯ рдФрд░ рдЗрдирд╡рд░реНрд╕реАрдлрд┐рдЬ рдХреЗ рд╕рд╛рде 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> {
    // Add custom methods here ...
}

:


рдЫрд╡рд┐


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())
    }

    // Add custom methods here ...

}

, , :


рдЫрд╡рд┐


: (, ) (, ).


, тАФ тАФ .


рдЫрд╡рд┐


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


рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдЖрдкрдХреЛ рдпрд╣ рд▓реЗрдЦ рдкрд╕рдВрдж рдЖрдпрд╛ рд╣реЛрдЧрд╛! рдХреГрдкрдпрд╛ рдЯрд┐рдкреНрдкрдгреА рдореЗрдВ рдпрд╛ рд▓реЗрдЦрдХ @RemoHJansen рдХреЛ рдЕрдкрдиреЗ рд╡рд┐рдЪрд╛рд░ рд╕рд╛рдЭрд╛ рдХрд░реЗрдВ ред


рдЕрдВрдд рддрдХ рдкрдврд╝рдиреЗ рд╡рд╛рд▓реЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдмреЛрдирд╕ рдПрдХ рдХрд╛рдо рдХреЛрдб рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рд╛рде рдПрдХ рднрдВрдбрд╛рд░ рд╣реИ ред


All Articles