#1 2013-05-03 13:23:18

Roberto Schneiders
Member
From: Santa Catarina, Brazil
Registered: 2012-09-19
Posts: 127
Website

TSQLModel - Managing dependencies

As I understand the documentation and examples, In a normal implementation of a ORM server i have a code like this (Probably this is implemented differently in real applications):

Server

uses
  pessoa;
var
  SQLModel: TSQLModel;
  AServerORM: TSQLRestServerDB;
begin
  SQLModel := TSQLModel.Create([TPessoa],'Entidades');   
  AServerORM := TSQLRestServerDB.Create(SQLModel,'BaseDeDados.db3',False);
  AServerORM.CreateMissingTables(0);
end;

Client:

uses
  Pessoa;

var
  SQLModel: TSQLModel;
  Database: TSQLHttpClient;
begin
  SQLModel := TSQLModel.Create([TPessoa],'Entidades');   
  Database := TSQLHttpClient.Create('localhost','888',  SQLModel );
end;

I see some problems:
  - The developer who is adding a new entity will have to know the structure of the framework. He will know that he needs to add the new entity in SQLModel;
  - The class that initializes the ORM server knows (uses) all entities. Every time you add another entity, this class will have to be modified and recompiled. This problem repeats itself on the client side.
  - The model (TSQLModel) is usually the same on both the server and the client. In this case there will be duplicate code and the developer will have to change in at least two places.
Thus, we violate some of the SOLID principles.

I'm looking for better implementations.

Just to contextualize. In my environment, I have two models (TSQLModel). One public and one private. Public entities are available to the client. Private entities will only be used on the server.

I thought about doing something like this:

A new unit, to encapsulate the public model (and another for the private model).

unit EntidadesPublicas;

interface

uses
  mORMot;

type
  TEntidadesPublicas = class
  private
    class var SQLModel: TSQLModel;
  public
    class procedure RegistrarEntidade(AEntidade: TSQLRecordClass);
    class function GetSQLModel: TSQLModel;
  end;

implementation

{ TEntidadesPublicas }

class function TEntidadesPublicas.GetSQLModel: TSQLModel;
begin
  Result := SQLModel;
end;

class procedure TEntidadesPublicas.RegistrarEntidade(AEntidade: TSQLRecordClass);
begin
  SQLModel.AddTable(AEntidade);
end;

initialization
  TEntidadesPublicas.SQLModel := TSQLModel.Create([],'Entidades');

finalization
  TEntidadesPublicas.SQLModel.Free;

The entities will be like this:

unit Pessoa;

interface

uses
  SynCommons,
  mORMot;

type
  TPessoa = class(TSQLRecord)
  private
    FSobrenome: RawUTF8;
    FNome: RawUTF8;
    FIdade: Integer;
  published
    property Nome: RawUTF8 read FNome write FNome;
    property Sobrenome: RawUTF8 read FSobrenome write FSobrenome;
    property Idade: Integer read FIdade write FIdade;
  end;

implementation

uses
  EntidadesPublicas;

initialization
  TEntidadesPublicas.RegistrarEntidade(TPessoa);

end.

Server side:

uses
  EntidadesPublicas;

var
  AServerORM: TSQLRestServerDB;
begin
  AServerORM := TSQLRestServerDB.Create(TEntidadesPublicas.GetSQLModel,'BaseDeDados.db3',False);
  AServerORM.CreateMissingTables(0);
end.

Client side:

uses
  EntidadesPublicas;

var
  Database : TSQLRestClientURI;
begin
  Database := TSQLHttpClient.Create('localhost','888', TEntidadesPublicas.GetSQLModel);
end.

Thus:
- When adding a new entity, the only thing the developer needs to worry about is to register it in the "Initialization" using the method "TEntidadesPublicas.RegistrarEntidade (TPessoa)"
- The class that initializes the server and the client does not need to be changed and recompiled. They do not know (uses) the entities.

PS: I do not like to use the initialization clause, but it was the only way I saw to do this. Any suggestions?

What do you think? How do you implement this in real applications?

Offline

#2 2013-05-03 14:36:45

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,238
Website

Re: TSQLModel - Managing dependencies

AFAIK the client model can not contain all the classes available on the server.
Only the classes the client needs are expected to be part of the model.
Unless you use a TSQLRecordReference kind of field.

Use case of the TSQLModel instance is to be defined in a given object context.
To ensure re-entrance of the classes.
Using such a global model will add a dependency, and will break the dependency inversion model for sure.

Using a shared unit in both client and server side is just a very good idea.
Due to the design of mORMot, you need only to define SynCommons and mORMot in the uses clause of this unit.
So it won't generate unused code on client side.
See all the samples of the framework, which use such a shared unit for defining all TSQLRecord classes, TSQLRestModel instance and interfaces for services.

Offline

#3 2013-05-03 14:44:03

lele9
Member
Registered: 2011-10-28
Posts: 170

Re: TSQLModel - Managing dependencies

see MainDemo.
you can find that ab use TFileServer and TFileClient classes like your purpose.

Offline

#4 2013-05-03 17:10:13

Roberto Schneiders
Member
From: Santa Catarina, Brazil
Registered: 2012-09-19
Posts: 127
Website

Re: TSQLModel - Managing dependencies

Sometimes I do not know if you're for or against my implementation.  Let's try to clarify.

ab wrote:

AFAIK the client model can not contain all the classes available on the server.
Only the classes the client needs are expected to be part of the model.
Unless you use a TSQLRecordReference kind of field.

I think there may be cases where the client has access to all the classes available on the server. But that's okay. Let's say that the model (SQLModel) client has only a part of the classes that are on the server model. This will slightly change my implementation. But it is still possible.

ab wrote:

Use case of the TSQLModel instance is to be defined in a given object context.
To ensure re-entrance of the classes.
Using such a global model will add a dependency, and will break the dependency inversion model for sure.

I do not understand what you mean by "Use case of the TSQLModel instance is to be defined in a given object context.";

My implementation does not differ so much from the implementation you used in the "CreateSampleModel". However, the "CreateSampleModel" has only one entity. In case you have multiple entities the implementation would be slightly different, I guess. And if not, the unit SampleData.pas (Ex 04) would have to know all the entities.

When you say that my implementation will add a dependency, you mean that all entities will depend on another unit, in my case, EntidadesPublicas.pas?
Regard this entities will have to be shared between several projects. Create a dependency of another unit, also shared, is a problem?

What would you change in my implementation?
You would drive EntidadesPublicas.pas know all the entities and create the model from them?

lele9 wrote:

see MainDemo.
you can find that ab use TFileServer and TFileClient classes like your purpose.

I'm sorry, but this demo is very confusing.

Last edited by Roberto Schneiders (2013-05-03 17:11:49)

Offline

#5 2013-05-07 08:33:12

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,238
Website

Re: TSQLModel - Managing dependencies

In fact, you can have several clients and servers in the same instance, e.g. for implementing proxies, with not the same TSQLModel content.
This is what I meant by "Use case of the TSQLModel instance is to be defined in a given object context".

I still do not understand why having all TSQLRecord classes in the clients TSQLModel is a big issue, even if the client does not use all of them.
Thanks to the rights policy of mORMot, you can have access to only the allowed TSQLRecord.
And thanks to Delphi compiler smart linking, it won't make your executable bigger.

Offline

Board footer

Powered by FluxBB