Receber por email
← Voltar para o catálogo

Delphi SOLID Patterns

SOLID implementation patterns for Delphi projects — Repository, Service, Factory, Strategy with constructor injection and interfaces

42estrelas
Atualizado há 7 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add delphicleancode/delphi-spec-kit

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/delphi-solid-patterns/svg)](https://www.skillteca.com.br/skills/delphi-solid-patterns)

Skills relacionadas

Ver todas de DevOps e Infra →

Alerta por categoria

Receba novas skills de DevOps e Infra toda segunda

1 email curto, só com as skills novas de DevOps e Infra. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Delphi SOLID Patterns — Skill

Use this skill when the user requests the creation of classes, services, repositories or any structure that follows SOLID principles in Delphi.

When to Use

  • When creating a new domain entity
  • When creating a repository (data access)
  • When creating a service (business logic)
  • When implementing a use case
  • When applying any design pattern (Factory, Strategy, Observer)

Repository Pattern

Repository Interface

unit MeuApp.Domain.Customer.Repository.Intf;

interface

uses
  System.Generics.Collections,
  MeuApp.Domain.Customer.Entity;

type
  ///<summary>
  ///Interface for client reading operations.
  ///</summary>
  ICustomerReadRepository = interface
    ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}']
    function FindById(AId: Integer): TCustomer;
    function FindAll: TObjectList<TCustomer>;
    function FindByCpf(const ACpf: string): TCustomer;
  end;

  ///<summary>
  ///Interface for client writing operations.
  ///</summary>
  ICustomerWriteRepository = interface
    ['{B2C3D4E5-F6A7-8901-BCDE-F12345678901}']
    procedure Insert(ACustomer: TCustomer);
    procedure Update(ACustomer: TCustomer);
    procedure Delete(AId: Integer);
  end;

  ///<summary>
  ///Complete repository interface combining reading and writing.
  ///</summary>
  ICustomerRepository = interface(ICustomerReadRepository)
    ['{C3D4E5F6-A7B8-9012-CDEF-123456789012}']
    procedure Insert(ACustomer: TCustomer);
    procedure Update(ACustomer: TCustomer);
    procedure Delete(AId: Integer);
  end;

implementation

end.

Implementation with FireDAC

unit MeuApp.Infra.Customer.Repository;

interface

uses
  System.SysUtils,
  System.Generics.Collections,
  FireDAC.Comp.Client,
  MeuApp.Domain.Customer.Entity,
  MeuApp.Domain.Customer.Repository.Intf;

type
  TCustomerRepository = class(TInterfacedObject, ICustomerRepository)
  private
    FConnection: TFDConnection;
  public
    constructor Create(AConnection: TFDConnection);

    { ICustomerReadRepository }
    function FindById(AId: Integer): TCustomer;
    function FindAll: TObjectList<TCustomer>;
    function FindByCpf(const ACpf: string): TCustomer;

    { ICustomerWriteRepository }
    procedure Insert(ACustomer: TCustomer);
    procedure Update(ACustomer: TCustomer);
    procedure Delete(AId: Integer);
  end;

implementation

constructor TCustomerRepository.Create(AConnection: TFDConnection);
begin
  inherited Create;
  if not Assigned(AConnection) then
    raise EArgumentNilException.Create('AConnection cannot be nil');
  FConnection := AConnection;
end;

function TCustomerRepository.FindById(AId: Integer): TCustomer;
var
  LQuery: TFDQuery;
begin
  Result := nil;
  LQuery := TFDQuery.Create(nil);
  try
    LQuery.Connection := FConnection;
    LQuery.SQL.Text := 'SELECT * FROM customers WHERE id = :id';
    LQuery.ParamByName('id').AsInteger := AId;
    LQuery.Open;

    if not LQuery.IsEmpty then
    begin
      Result := TCustomer.Create(LQuery.FieldByName('name').AsString);
      Result.Id := LQuery.FieldByName('id').AsInteger;
    end;
  finally
    LQuery.Free;
  end;
end;

//... other methods follow the same pattern

Service Pattern

Service Interface

unit MeuApp.Application.Customer.Service.Intf;

interface

uses
  MeuApp.Domain.Customer.Entity;

type
  ICustomerService = interface
    ['{D4E5F6A7-B8C9-0123-DEF0-234567890123}']
    function GetById(AId: Integer): TCustomer;
    procedure CreateCustomer(const AName: string; const ACpf: string);
    procedure UpdateCustomer(ACustomer: TCustomer);
    procedure DeleteCustomer(AId: Integer);
  end;

implementation

end.

Service Implementation

unit MeuApp.Application.Customer.Service;

interface

uses
  System.SysUtils,
  MeuApp.Domain.Customer.Entity,
  MeuApp.Domain.Customer.Repository.Intf,
  MeuApp.Application.Customer.Service.Intf;

type
  ///<summary>
  ///Customer service with dependency validation and orchestration.
  ///</summary>
  TCustomerService = class(TInterfacedObject, ICustomerService)
  private
    FRepository: ICustomerRepository;
    procedure ValidateCpf(const ACpf: string);
  public
    constructor Create(ARepository: ICustomerRepository);
    function GetById(AId: Integer): TCustomer;
    procedure CreateCustomer(const AName: string; const ACpf: string);
    procedure UpdateCustomer(ACustomer: TCustomer);
    procedure DeleteCustomer(AId: Integer);
  end;

implementation

constructor TCustomerService.Create(ARepository: ICustomerRepository);
begin
  inherited Create;
  if not Assigned(ARepository) then
    raise EArgumentNilException.Create('ARepository cannot be nil');
  FRepository := ARepository;
end;

function TCustomerService.GetById(AId: Integer): TCustomer;
begin
  Result := FRepository.FindById(AId);
  if not Assigned(Result) then
    raise EEntityNotFoundException.CreateFmt('Customer not found: %d', [AId]);
end;

procedure TCustomerService.CreateCustomer(const AName: string; const ACpf: string);
var
  LCustomer: TCustomer;
  LExisting: TCustomer;
begin
  ValidateCpf(ACpf);

  LExisting := FRepository.FindByCpf(ACpf);
  if Assigned(LExisting) then
    raise EBusinessRuleException.CreateFmt('CPF already registered: %s', [ACpf]);

  LCustomer := TCustomer.Create(AName);
  try
    LCustomer.Cpf := ACpf;
    FRepository.Insert(LCustomer);
  except
    LCustomer.Free;
    raise;
  end;
end;

procedure TCustomerService.ValidateCpf(const ACpf: string);
const
  CPF_LENGTH = 11;
begin
  if ACpf.Trim.IsEmpty then
    raise EValidationException.Create('CPF cannot be empty');
  if ACpf.Length <> CPF_LENGTH then
    raise EValidationException.CreateFmt('CPF must have %d digits', [CPF_LENGTH]);
end;

//...other methods

Factory Pattern

unit MeuApp.Infra.Factory;

interface

uses
  FireDAC.Comp.Client,
  MeuApp.Domain.Customer.Repository.Intf,
  MeuApp.Application.Customer.Service.Intf;

type
  ///<summary>
  ///Factory to create instances of services and repositories.
  ///</summary>
  TServiceFactory = class
  public
    class function CreateCustomerRepository(AConnection: TFDConnection): ICustomerRepository;
    class function CreateCustomerService(AConnection: TFDConnection): ICustomerService;
  end;

implementation

uses
  MeuApp.Infra.Customer.Repository,
  MeuApp.Application.Customer.Service;

class function TServiceFactory.CreateCustomerRepository(AConnection: TFDConnection): ICustomerRepository;
begin
  Result := TCustomerRepository.Create(AConnection);
end;

class function TServiceFactory.CreateCustomerService(AConnection: TFDConnection): ICustomerService;
var
  LRepo: ICustomerRepository;
begin
  LRepo := CreateCustomerRepository(AConnection);
  Result := TCustomerService.Create(LRepo);
end;

Strategy Pattern

unit MeuApp.Domain.Tax.Strategy;

interface

type
  ///<summary>
  ///Interface for tax calculation strategies.
  ///</summary>
  ITaxCalculator = interface
    ['{E5F6A7B8-C9D0-1234-EF01-345678901234}']
    function Calculate(ABaseValue: Currency): Currency;
    function GetDescription: string;
  end;

  TSimplesTaxCalculator = class(TInterfacedObject, ITaxCalculator)
  public
    function Calculate(ABaseValue: Currency): Currency;
    function GetDescription: string;
  end;

  TLucroPresumidoTaxCalculator = class(TInterfacedObject, ITaxCalculator)
  public
    function Calculate(ABaseValue: Currency): Currency;
    function GetDescription: string;
  end;

implementation

{ TSimplesTaxCalculator }

function TSimplesTaxCalculator.Calculate(ABaseValue: Currency): Currency;
const
  SIMPLES_RATE = 0.06;
begin
  Result := ABaseValue * SIMPLES_RATE;
end;

function TSimplesTaxCalculator.GetDescription: string;
begin
  Result := 'Simples Nacional (6%)';
end;

{ TLucroPresumidoTaxCalculator }

function TLucroPresumidoTaxCalculator.Calculate(ABaseValue: Currency): Currency;
const
  LUCRO_PRESUMIDO_RATE = 0.15;
begin
  Result := ABaseValue * LUCRO_PRESUMIDO_RATE;
end;

function T
Compartilhar X LinkedIn