You are not logged in.
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?
I have a question regarding the service register.
Server.ServiceRegister(TServiceCalculator,[TypeInfo(ICalculator)],sicShared)
Client.ServiceRegister([TypeInfo(ICalculator)],sicShared);I am forced to register services on both the server and the client. Ok, I suppose we need the interfaces information in the Client Layer, and so need to run the client ServiceRegister. However, do not understand why I have to reenter the TServiceInstanceImplementation (eg sicShared)!
And if I inform different things on the client and server, what can happen?
I was just looking at it. Unfortunately I installed the Trial in the past to make tests with REST and now, even installing a new version, I can not use because the trial period has ended.
I'll have to find another way to test that.
Thanks for the tips.
When I said SOA was referring to REST.
Unfortunately this Web Service Toolkit is very complicated.
I can not even install the components in Lazarus, simply does not compile. What about the components for Delphi (to generate the WSDL)? I have to do everything via command line?
The examples did not compile in XE2. It is extremely frustrating.
Thanks for the help Arnaud.
We are evaluating the two options. We know the benefits of SOA. But the SOAP also has some advantages. How we work with many typed objects between Delphi and Java application server, SOAP facilitates a bit.
It's a long discussion! But we are looking for the best alternative for our case.
Hello
I've been doing some testing with Java server. We are evaluating the possibility of using SOAP.
I did a test with a data volume of 10Mb. A java application took around 3 seconds to grab the data from the server. A C# application as well. An delphi application ranging from 100 to 150 sec to bring the same result.
I'm doing tests over a gigabit network. Has a certain discrepancy in these tests because the Delphi API apparently does not support GZIPover SOAP. But as the volume of data is small compared to the band, this should not be interfering so much.
I did a profiling in delphi application and noticed that almost all processing is done in msxml6.dll. Strangely even microsoft chose not to use it. The C# application does not use this DLL.
Does anyone know any alternative to SOAP client for Delphi?
As far as I know mORMot only works with Rest, but maybe someone has worked with SOAP ...
Now I began to understand! ![]()
In your opinion should I design my software as in the first diagram?
I understand that TSQLRecord and TSQLRest classes, as well as the types like RawUTF8, are a kind abstract types (Super types). That is ok. But I think we're looking at the problem from different perspectives. Of course, we're on totally different levels of knowledge, but let me try to explain why I thought in the abstraction layer.
According to the uncle bob lecture ...
My software must not know the frameworks I'll use (mORMot). The frameworks need to be plugged in the software. I must not develop my software based on a framework. If I do it'll be stuck in this framework.
So the question is, how do I design my software so that it does not know the framework? How am I going to plug the framework in my software.
I thought creating an abstraction layer could solve this.
If I use any class or type of framework directly into my software I will be tying my software to the framework. Or not?
Imagine that in 5 years we decided to change ORM (I see no reason for this, just a guess), using the mORMot.pas and SynCommons.pas units I have to refactor the whole system.
I understand that mORMot has your own abstraction layers, but these layers, willingly or not, are part of the framework.
Do you think that creating a new layer of abstraction is the wrong way?
I'm having a hard time imagining how to separate the layers using DDD and mORMot. I wanted to create a diagram (fictional) where I could see more clearly the layers and organizing things with DDD. ![]()
I know that's abuse, but maybe it would be interesting to create a diagram showing an architecture using a simple software with mORMot. Something more like the real world.
Could contain:
- A CRUD
- A report
- A business rule
I think it would be a very useful diagram showing how objects (entities and objects of value) fit with Repositories using mORMot on a project with DDD. At which layer the framework classes fit, etc...
Thank you.
I'm a little confused now. I had read your blog post about DDD, but it is unclear to me. I'm new in the DDD world.
I'm sorry, but I have much more questions now.
You say "AFAIK, in DDD the "Persistence agnostic" principle forbid the repository to be implemented neither in the Domain Layer, nor in the Application Layer."
Maybe I misunderstood, but I understand that the Repository (explained by Eric) it is an object of the domain layer. Eric says that are domain concepts. Of course the persistence with the database would be in infrastructure. But the repositories will contain links to the infrastructure layer, where persistence is implemented.
In this book (Page 54 and 56) has these statements:
"It can be noted that the implementation of a repository can be closely liked to the infrastructure, but that the repository interface will be pure domain model.
...Another way this is noted is that Factories are “pure domain”, but that Repositories can contain links to the infrastructure, e g the database."
You say "You should not be afraid to use SynCommons.pas in your clients." about the abstraction layer.
Ok. As I understand the TSQLRecord and TSQLRest already are abstraction layers. They are part of an abstraction layer of the framework. So I do not need to create my own abstraction layer. I understood correctly?
Thanks for your patience.
I want to start a discussion about clean architecture
. This is not a discussion related to mORMot in particular.
After see this lecture from Uncle Bob i started to think about it. Take a look http://vimeo.com/43612849
Initially the architecture of my software would be this way:
We can consider the TSQLHTTPClient as a repository (In Domain Driven Design)? I'm not sure, but I believe so. The biggest problem is that in this case the repository is the infrastructure layer. That seems very wrong. What do you think?
For now I will consider that I can use as a repository.
Back to the topic of clean architecture. One of the things that Uncle Bob talks in the lecture is more or less like this: Your software (your domain) should not know the frameworks. Do not attach your software to frameworks. "Make implementation details swappable."
Considering everything that Uncle Bob said, do you think this architecture is clean?
I do not think so.
In this way the software is strongly linked to the framework. The domain objects directly accesses the framework. The entities inherit from framework classes.
What will happen with your software if you, for whatever reason, need to change the framework?
So, the architecture is not clean.
I'm new at this, I want your help to try to create a clean architecture.
How about creating a abstraction layer? like this
Note that I include in the abstraction layer even the primitive types of mORMot like RawUTF8. How is a specific type of mORMot, if I use it throughout the software when you need to change it, I'll have to change the whole system.
I created a class called TAbstractRecord. This class inherits from TSQLRecord and basically is an empty class. Does absolutely nothing right now. In case of changing the framework I just need to adapt this classes to the new framework and my entities not need to be changed. Obviously it may not be so easy, but it's a start.
Another class of abstraction is the TRepository. This class encapsulates all communication with mORMot objects like TSQLHTTPClient. If the persistence technology change, I need to adapt this class, and not the entire system.
A question about DDD. The class TSQLHTTPClient seems like a generic repository that works for all types of entities. I do not know exactly if this goes against the DDD or not. What do you think?
Say it fits the idea of DDD. Still, there will be cases in which it is better to create specific repository. Especially if you have a aggregate.
In this case I think that the architecture would be like this:
What do you think of these ideas? What do you think by clean architecture?
I still have many questions about DDD and Clean Architecture, hopefully we can resolve some here. ![]()
So...
In Domain Driven Design world, thinking about a clean architecture:
1 - We can consider the TSQLHttpClient as a repository?
1.1 - The repository can stay in the infrastructure layer?
1.1 - Is it correct have a generic repository, for all entities?
2 - You think I'm in the right way by creating abstraction layers?
Ok. Thank you.
Thank you very much. I will check this out.
I don't found the "execute" method in my client class (TSQLHttpClient).
I tried this method, but not work for me.
My code is like this:
Table := Client.List([], 'Select * from SGRUSR01');The result is nil.
I dont have de TSQLRecord classes for this tables.
You have another idea?
... and about those codes i posted (Services), its possible do that?
OK. I don't know how work with Service-oriented architecture of mORMot yet, i have to study this.
I can't do this?
IServiceTest = Interface(IInvokable)
['{638398D0-C0B5-4E1B-A46F-D30D26185477}']
function GetSQLTableJSON(ASQL:RawUTF8):TSQLTableJSON;
end;
TServiceTest = class (TInterfacedObject, IServiceTest)
function GetSQLTableJSON(ASQL:RawUTF8):TSQLTableJSON;
end;or this?
IServiceTest = Interface(IInvokable)
['{638398D0-C0B5-4E1B-A46F-D30D26185477}']
procedure GetSQLTableJSON(ASQL:RawUTF8; out Result:TSQLTableJSON);
end;
TServiceTest = class (TInterfacedObject, IServiceTest)
procedure GetSQLTableJSON(ASQL:RawUTF8; out Result:TSQLTableJSON);
end;That's not make sense for me ManUn.
We have to do that in ORM approach.
It is possible to use this method (TSQLDBConnectionProperties.Execute) on client side?
Basically i need a TSQLTableJson from a external complex table. This table not have a TSQLRecord mapping.
I think I found it.
in TSQLRestClientURI.UpdateFromServer method the "T.InternalState" is equal to "State" (ServerInternalState). In this case the conditional test in line 20531 don't pass and nothing happens.
Any tips of which method debug in the server?
i have the same problem.
I still have with this problem with "UpdateFromServer".
Can anyone help me?
With the last revision (2013-02-07 : 20:51) i have some compilation problems:
[DCC Error] SynCommons.pas(11618): E2441 Inline function declared in interface section must not use local symbol 'GetHighUTF8UCS4'
[DCC Warning] SynCommons.pas(13537): W1000 Symbol 'faVolumeID' is deprecated
PS: I'm using Delphi XE2 Update 4.
About memory consumption. Ok
...and about UpdateFromServer?
That same software. The table tpeople has 10 thousand records. Every time a customer requests the data (CreateAndFillPrepare) memory consumption of the server increases almost 1 MB. Debugging realized that memory allocation is done in JSON parser. But I could not figure out why this memory is not freed.
There are no memory leaks after closing the server.
Now tested with SQLite. Apparently, the behavior is the same.
I had been using '1=1' because this parameter is required. Would not be better to let a default value for this parameter?
one more question.
I'm using the command UpdateFromServer to fetch updates the list of people. It's not working for me.
code:
var
refreshed:boolean;
begin
if Assigned(TableToGrid) then
begin
Client.UpdateFromServer([PeopleList], refreshed);
// I also tried Client.UpdateFromServer([PeopleList.FillTable], refreshed);
if refreshed then
begin
TableToGrid.Refresh();
DrawGrid1.Refresh;
end;
end
else
begin
PeopleList := TPeople.CreateAndFillPrepare(Client, 'ID<>0');
TableToGrid := TSQLTableToGrid.Create(DrawGrid1, PeopleList.FillTable, Client);
end;How should I use?
UpdateFromServer([PeopleList], refreshed)
or
UpdateFromServer([PeopleList.FillTable], refreshed)I add a new record to the database via IBExpert. When executing this method (UpdateFromServer) the variable "refreshed" is false.
On the server log I notice that he does not perform a select on the database.
I suppose maybe this method only works when the table is changed exclusively thru the ORM. (Documentation: make use of the InternalState function to check the data content revision)
In this case, there is some method to update a table that eventually can be changed by another application?
I know that any manipulation in the database must pass through the server (ORM), but as we are trying to migrate an old application, it is not possible at the moment.
It worked!
Yes, is a external table.
I captured this command via debug.
SELECT ID,Name,Age,BirthDate FROM People WHERE 1=1I run this command via IBExpert and works.
I have a problem with the command CreateAndFillPrepare.
I have a mORMot server. Basically it is:
fConnection := TODBCConnectionProperties.Create('teste', 'roberto', 'sysdba', 'password');
VirtualTableExternalRegisterAll(Model,fConnection);
fConnection.MainConnection.Connect;
if fConnection.MainConnection.IsConnected then
begin
fServer := TSQLRestServerDB.Create(Model, 'test.db3', false);
fServer.CreateMissingTables;
HTTPServer := TSQLHttpServer.Create('888', [fServer]);
end;If I type this in the browser:
http://192.168.3.134:888/root/people/I get a JSON with all table data. As expected.
In Delphi client I can not get the same result. When performing the method CreateAndFillPrepare of Tpeople class the data is not returned.
Client code:
Client := TSQLHttpClient.Create('192.168.3.134', '888', Model);
if not Client.ServerTimeStampSynchronize then
ShowMessage(Client.LastErrorMessage);
...
PeopleList := TPeople.CreateAndFillPrepare(Client, '1=1');The server logs the following information:
20130205 10124217 - TSQLRestServerDB(0225F9D0). 00.001.773
20130205 10124948 + TSQLRestServerDB(0225F9D0).root
20130205 10124948 cache TSQLDatabase(0230AC20) not in cache
20130205 10124948 EXC ESQLite3Exception ("no such column: ID") at 004BC807 stack trace 0048D8AF 0049070B 004B8CF0 004B810B 004B7706 0044A7BD 00408006 74D5339A 770B9EF2 770B9EC5
20130205 10124948 res null
20130205 10124948 srvr GET root ERROR=400 (Bad Request)
20130205 10124948 - TSQLRestServerDB(0225F9D0). 00.004.176Any tips?
I just do not have the server started.
After this line
Client:= TSQLHttpClient.Create = ('127 .0.0.1 ', '888', Model);the Client.LastErrorException is nil.
sure.
Good to know that has more Brazilians using mORMot.
Thanks Arnaud.
I have some questions.
I just do not know when the server connection is working or not.
I have this code:
Client:= TSQLHttpClient.Create = ('127 .0.0.1 ', '888', Model);Is there any way to test if a connection to the server is ok?
In the example that I put in this post http://synopse.info/forum/viewtopic.php?id=1053
There is still no solution for this, right?
Got it.
Thank you.
Now it's a bit clearer to me. ![]()
I will try to implement an example.
Thank you.
Why this code:
var
Municipio: TMunicipio;
Pais: TPais;
Estado: TEstado;
begin
Estado := TEstado.Create;
Estado.Sigla := 'SC';
Estado.Nome := 'Santa Catarina';
Pais := TPais.Create;
Pais.Descricao := 'Brasil';
Municipio := TMunicipio.Create;
Municipio.Descricao := 'São Miguel D''Oeste';
Municipio.DescricaoReduzida := 'SMO';
Municipio.Cep := 89899000;
Municipio.DDD := 49;
Municipio.Estado := Estado;
Municipio.Pais := Pais;
fClient.Add(Estado, true);
fClient.Add(Pais, true);
fClient.Add(Municipio, true);
end;writes this information in the tables of the database?
TABLE: PAÍS
ID = 1
DESCRICAO = Brasil
TABLE: ESTADO
ID = 1
SIGLA = SC
NOME = Santa Catarina
TABLE: MUNICÍPIO
ID = 1
DESCRICAO = São Miguel D'Oeste
DESCRICAOREDUZIDA = SMO
CEP = 89899000
DDD = 49
ESTADO = 57447296
PAIS = 57447328Should not write ESTADO = 1 e PAIS = 1 in table MUNICIPIO?
Before writing MUNICIPIO the objects ESTADO and PAIS have their IDs.
It may be necessary have more than one machine providing access to ORM.
In that case I would have to enable the property EngineAddUseSelectMaxID? Or the servers communicate with each other?
Do you have any examples of how to implement this?
Is there any mechanism for load balancing?
Can I have two executables running ORM (server) on different computers?
Thanks Arnaud.
I also do not like packages . But our project is really big, I need to evaluate this option
.
That is, you can have the same executable be stand-alone, client, full server, services server, db server...
It was exactly what I was talking about. ![]()
Interesting. Apparently only works in Internet Explorer.
In Chrome just does not work. In Firefox the following message appears:
This site () is requesting data storage on your computer for offline use
By clicking on allow nothing happens, the message remains on the screen. Connect doesn't work.
Excellent. Thank you for sharing. ![]()
Maybe we can include these classes in the framework, what do you think Arnaud?
I'm not able to compile in version 1.1.0.371 Smart Mobile Studio
:
Syntax Error: There's no accessible member with name "properties"! [line: 613, column: 20, file: mORMotServer]
In my case the example is not working. By clicking on connect nothing happens. If I find something I post here.
Thinking of a n-tier structure, where the layers would Presentation, Application, Domain Model, Data Persistence, Cross-Cutting. As shown on page 58 of the documentation (1.18).
Is possible to run each tier on different computers? Of course the presentation layer runs on client machines. I refer to the server-side layers.
At first I thought of separating these layers in modules (BPL Packages) on my server. But what if the computer where the server is running does not have the resources to support all modules, I could put, for example, the data persistence layer (ORM) on another machine?
How would I do that?
The diagram in the documentation makes perfect sense to me. But I do not know how to organize the software modules in this way, could you give me an example?
In the software you maintain Arnaud, how you divide the application? You use BPL packages?
Not bad ![]()
is good to see that other people have reached similar conclusions and results.
What is the amount of data to be stored, at each level?
The current demand is around 3000 supplies per day . Each supply is a record in the table.
Thinking in an extremely high demand, would to put something like 5 records per second.
But this is only the records that are returned by CUB. These records suffer manipulations in client applications.
The client applications need to maintain a list of supplies. This list is updated every 1 second. I believe that we will use cache here.
How fast is it expected to be retrieved/stored?
I think it should not take more than 200ms to return/update a record.
Is it just a record of history values?
I do not know if I got it right.. It is only one record per supply. But this record has a status.
This week I'm participating in the planning of a new project that has some pretty interesting requirements.
I thought, will be the first project using mORMot.
I'll try to explain what the software will do for you to understand my problem.
It is a software for gas stations (He actually already exists, we are reformulating some things). We are refactoring the communication with CUB (hardware hub). The CUB is responsible for concentrating the supply of information sent by the fuel pumps. There will be a software (server) that will communicate with the CUB, ordering and record this information in a local database (SQLite). There may be several stations clients requesting this information (intend to use mORMot ORM for this).
We need to implement a solution in case the server has an issue (eg A meteor falls on him
)
The CUB information are not persisted by him, ie, once requested by the server, are excluded from hardware memory. In this case, if the server machine has problems we have lost the data, and that sucks.
We want a local database because the main database (firebird) may be outside the local network (eg Internet) and connect to it would mean a huge detriment to performance.
One of our ideas was to create a mechanism for data persistence. The information stored in the local database (SQLite) are persisted (in the background) for a main base (firebird / postgresql). Of course there is no 100% guarantee, but it's something.
Is there a mechanism that allows the mORMot do this?
Do you have any other ideas?
okay.
When the driver is ready I can help you on the tests.
Most probably the answer to my question is in the documentation, but I have not found. ![]()
As all communication is stateless I have to worry about dealing with possible connection problems at all locations that the framework will call the server, right?
For example, I have a code responsible for making a change to a table (in client).
if Client.Update(People) then
ShowMessage('Yeah')
else
ShowMessage('Ouch. Something very bad happened. But what really happened?')The update method returns a boolean telling me if it worked. How do I know what went wrong in case I receive a false response?
Is there any method for me to test if the communication with the server is running?
With this property enables mORMot took 31.5s.
Amazing.
I do not know what to say. Simply Fantastic. ![]()
DBExpress: 5,7s
Aurelius: 28,4s
mORMot: 1,3s
I would like to test with this setting EngineAddUseSelectMaxID. I do not know how. this property is in TSQLRestServerStaticExternal class. How do I access this object from my client (TSQLRestClientDB)?
Thank you very much.
It's working. Thank you.
Thanks Arnaud.
I think it exists in the SQLite database but not in the external database (Firebird).
If I find something I notice.