Desktop and ModularBr

Simplifying the usage of ModularBr with desktop applications through middleware.

ModularBr is a versatile framework that goes beyond its integration with REST frameworks for building RESTful APIs. It is also well-suited for client/server (desktop) project development. With its dependency injection and bind exporting functionalities, ModularBr provides a modular approach to Delphi application development. This approach can be applied to projects involving client-server communication, allowing developers to benefit from the modularity, code reuse, and dependency management offered by ModularBr. By adopting ModularBr in client/server projects, developers can enhance code organization, simplify maintenance, and facilitate scalability of desktop applications.

Just as in RESTful applications, in desktop applications, the AppModule also plays a crucial role as the entry point for utilizing ModularBr.

uses
  dmfbr.modular,
...

procedure TFormPing.PingClick(Sender: TObject);
var
  LRouteHandler: TPingRouteHandler;
begin
  LRouteHandler := TPingRouteHandler.Create;
  try
    ShowMessage(LRouteHandler.Ping);
  finally
    LRouteHandler.Free;
  end;
end;

procedure TFormPing.FormCreate(Sender: TObject);
begin
  Modular.Start(TAppModule.Create);
end;

In the AppModule, we define the initial routes.

...
function TAppModule.Routes: TRoutes;
begin
  Result := [RouteModule('/ping', TPingModule)];
end;

We define the module for the mapped route '/ping'.

unit ping.module;

interface

uses
  ping.controller,
  ping.service,
  dmfbr.module;

type
  TPingModule = class(TModule)
  public
    function Imports: TImports; override;
    function Binds: TBinds; override;
    function Routes: TRoutes; override;
    function RouteHandlers: TRouteHandlers; override;
    function ExportedBinds: TExportedBinds; override;
  end;

implementation

{ TPingModule }

function TPingModule.Binds: TBinds;
begin
  Result := [Bind<TPingService>.Factory,
             Bind<TPingController>.Singleton];
end;
function TPingModule.ExportedBinds: TExportedBinds;
begin
  Result := [];
end;

function TPingModule.Imports: TImports;
begin
  Result := [];
end;

function TPingModule.RouteHandlers: TRouteHandlers;
begin
  Result := [];
end;

function TPingModule.Routes: TRoutes;
begin
  Result := [];
end;

end.

The Route Handlers, or route handlers, are responsible for invoking the desired modules based on the routes defined in the AppModule.

unit ping.route.handler;

interface

uses
  SysUtils,
  result.pair,
  dmfbr.modular,
  ping.controller,
  dmfbr.route.handler;

type
  TPingRouteHandler = class(TRouteHandler)
  protected
    procedure RegisterRoutes; override;
  public
    function Ping: string;
  end;

implementation

uses
  dmfbr.route.abstract;

{ TPingRouteHandler }

procedure TPingRouteHandler.RegisterRoutes;
begin

end;

function TPingRouteHandler.Ping: string;
var
  LResultPing: string;
  LResultRoute: TResultPair<Exception, TRouteAbstract>;
begin
  LResultPing := '';
  LResultRoute := Modular.LoadRouteModule('/ping');
  try
    LResultRoute.TryException(
      procedure (Error: Exception)
      begin
        // Failure
        LResultPing := Error.Message;
        Error.Free;
      end,
      procedure (Route: TRouteAbstract)
      begin
        // Success
        LResultPing := Modular.Get<TPingController>.Ping;
      end);
   finally
     Modular.DisposeRouteModule('/ping');
   end;
  Result := LResultPing;
end;

end.

From this point onwards, the choice of the pattern to be adopted is entirely up to you. ModularBr provides complete freedom and flexibility for you to decide which pattern you want to use. In this documentation, we will present an example using the concepts of controller and service, following the principle of single responsibility, in order to illustrate the implementation. However, it is important to note that you have the option to choose other patterns according to your individual needs and preferences.

The Controller

unit ping.controller;

interface

uses
  ping.service;

type
  TPingController = class
  private
    FService: TPingService;
  public
    constructor Create(const AService: TPingService);
    destructor Destroy; override;
    function Ping: string;
  end;

implementation

{ TPingController }

constructor TPingController.Create(const AService: TPingService);
begin
  FService := AService;
end;

destructor TPingController.Destroy;
begin
  FService.Free;
  inherited;
end;

function TPingController.Ping: String;
begin
  Result := FService.Ping;
end;

end.

The Service

unit ping.service;

interface

type
  TPingService = class
  public
    function Ping: string;
  end;

implementation

{ TPingService }

function TPingService.Ping: string;
begin
  Result := 'Pong';
end;

end.

Last updated