N-tier architecture: Difference between revisions

From OpenPetra Wiki
Jump to navigation Jump to search
m (1 revision)
 
 
(47 intermediate revisions by 5 users not shown)
Line 1: Line 1:
== Glue between Server and Client ==
= Client/Server Architecture =
* the code for interfaces and instantiators is generated
There are several options how to design a client/server architecture.
* first edit file csharp/ICT/Petra/Definitions/NamespaceHierarchy.xml (TODO?: change to yml for easier readability)
* It depends on what environment you are using (bandwidth, datalink speed, etc) and on your requirements (supported operating systems etc).
* then run nant generateGlue for the first time
* On the other hand you want to try to keep the logic in one place (the server), so that several types of clients can connect the one server.  
* then you can create a UIConnector .cs file; the constructor will be added to the Instantiator and to the interface the next time you run nant generateGlue
* Also access to the database should go all through the server, so that you don't need to create a database user for each of your users, and access permissions are handled by the server.
** a method that should not be added to the interface, should either be private, or have comment [NO-REMOTING] (with 3 slashes)
 
* you can also add a method to the instantiator, and put its codeblock in a ManualCode region; next time you generateGlue, the method will be added to the interface
Several types of clients:
* Standalone: this is for single user systems; they don't need to know that a server is started in the background.
* Fat client, Rich client: this is in our case a Winforms application, that connects to a server. The client can handle the logic, but perhaps we want to keep it still simple on the client. Updates/Patches to the client need to be run semi automatically.
* Slim Client: this could be a web client; it requires no installation effort.
** Rich Internet applications (RIAs) look like desktop applications, but run in the web browser
 
Another topic is the server itself:
* for our .net remoting setup, we have an Appdomain for each client, ie. all the dlls are loaded for every connected client
** this helps with server crashes: if one client crashes, the other clients can continue with work
** on the other hand, this uses a lot of memory
* for the SOAP/Web service setup of the server, there should be much better scalability
* how much should the server keep track of the state of the client? stateful vs stateless


=== different ways of accessing data on the server ===
= Glue between Server and Client =
== different ways of accessing data on the server ==
* UIConnectors: only used for screens; keep an object on the server for each screen
* UIConnectors: only used for screens; keep an object on the server for each screen
** advantage: server knows about the client (stateful); minimum data transfer, diffs are enough.
* LogicConnectors: keeps an object on the server side; needed for progress bar; eg. Report Calculation
* LogicConnectors: keeps an object on the server side; needed for progress bar; eg. Report Calculation
* ServerLookup: used for static methods; no object is held on the server; eg. Verify Partner in ExperimentingClient; UserDefaults uses ServerLookups internally
* ServerLookups: used for static methods; no object is held on the server (stateless); eg. Verify Partner in ExperimentingClient; UserDefaults uses ServerLookups internally
** WebConnectors: very similar to ServerLookup (stateless); easy generation of interface and instantiator, and winforms
* Cacheable: also static methods; they are not called by the developer, but by the Cachemanager on the server and the client side, when you are accessing a cached table
* Cacheable: also static methods; they are not called by the developer, but by the Cachemanager on the server and the client side, when you are accessing a cached table




=== How to create a new Serverlookup ===
Depending on its namespace and for server lookups you can use:
# Ict.Petra.Server.MPartner.Partner.ServerLookups
nant will collect all "public static <functionName />(<parameterList />)" functions and include their headers into the automatic generated file "csharp\ICT\Petra\Shared\lib\Interfaces\Partner.Interfaces.cs" and "csharp\ICT\Petra\Server\lib\MPartner\Instantiator.AutoHierarchy.cs".
So on the server side you have to
* use a valid namespace
* "public static" to define the function
If you want to call this function by the client you have to use:
# TRemote.MPartner.Partner.ServerLoookups.<functionName />(<parameterList />)
(The namespaces on the server and the client are mapped one-to-one)
== code generation ==
* the code for interfaces and instantiators is generated
* run nant generateGlue for the first time
* to add new modules (on the level of MFinance, MCommon, etc), search for REMOTINGURL_IDENTIFIER_MCOMMON in ICT/Petra/Shared/Constants.cs and ICT/Petra/Server/app/Core/ClientAppDomainConnector.cs and create similar lines of code for the new module
* then you can create a UIConnector .cs file; the constructor will be added to the Instantiator and to the interface the next time you run nant generateGlue
* UIConnector classes are inside a namespace ending with UIConnectors, and implementing an interface, which will be autogenerated by <code>nant generateGlue</code>. The class name should end with UIConnector
* a method that should not be added to the interface, should either be private, or have the attribute [NoRemoting]
* Web connector methods are the easiest - they will be added to the interface and the instantiator, and even the winforms: just make sure to add your method to the Webconnector class; run nant generateGlue, it will create the interface and instantiator for you
** nant generateWinforms will add functions to edit windows, eg. for retrieving data and saving data, using webconnectors
* WebConnector files are identified by their namespace ending with WebConnectors, and the class name ends in WebConnector. There is no interface, because the methods are static, and that won't work for interface methods.
* Namespaces in auto-generated <code>*.Interfaces-generated.cs</code> files: Any Namespace that is needed for the signature (e.g. for the Return Value or the Arguments) of a public Method of a WebConnector or UIConnector and that isn't listed in the file <code>\inc\template\src\ClientServerGlue\Interface.cs</code> in the code section above <code>{#USINGNAMESPACES}</code> needs to be manually added to the file <code>\csharp\ICT\Petra\Shared\lib\Interfaces\InterfacesUsingNamespaces.yml</code>! (You'll see the pattern on how to do this once you open the file.)
== todo ==
* Todo: something special about UIConnectors; extra things are generated for UIConnectors
* Todo: something special about UIConnectors; extra things are generated for UIConnectors
* the others are implemented manually at the moment
* the others are implemented manually at the moment
Line 23: Line 69:


* Table Maintance: use UIConnector
* Table Maintance: use UIConnector
= Data Processing in an n-tier architecture =
* example 1: create a new partner: new partner key is generated, will never be reused, even if the new partner is never saved to the database
* example 2: create a new GL batch; it is important to have consecutive batch numbers; cancel batch if it is not needed anymore; batch is stored with status "cancelled"
* example 3: AP Document is created with negative AP number, proper AP number is only assigned on submitting changes
* options: create new row on server, or on client
* options: for complex screens with several tabs, store the whole Typed Data Set, or each tab on its own?
* names that the generateWinforms is looking for in the server: Create<TableName>, Find<TableName>, Save<TableName>, Save<TypedDataSet>

Latest revision as of 21:05, 11 Mayıs 2022

Client/Server Architecture

There are several options how to design a client/server architecture.

  • It depends on what environment you are using (bandwidth, datalink speed, etc) and on your requirements (supported operating systems etc).
  • On the other hand you want to try to keep the logic in one place (the server), so that several types of clients can connect the one server.
  • Also access to the database should go all through the server, so that you don't need to create a database user for each of your users, and access permissions are handled by the server.

Several types of clients:

  • Standalone: this is for single user systems; they don't need to know that a server is started in the background.
  • Fat client, Rich client: this is in our case a Winforms application, that connects to a server. The client can handle the logic, but perhaps we want to keep it still simple on the client. Updates/Patches to the client need to be run semi automatically.
  • Slim Client: this could be a web client; it requires no installation effort.
    • Rich Internet applications (RIAs) look like desktop applications, but run in the web browser

Another topic is the server itself:

  • for our .net remoting setup, we have an Appdomain for each client, ie. all the dlls are loaded for every connected client
    • this helps with server crashes: if one client crashes, the other clients can continue with work
    • on the other hand, this uses a lot of memory
  • for the SOAP/Web service setup of the server, there should be much better scalability
  • how much should the server keep track of the state of the client? stateful vs stateless

Glue between Server and Client

different ways of accessing data on the server

  • UIConnectors: only used for screens; keep an object on the server for each screen
    • advantage: server knows about the client (stateful); minimum data transfer, diffs are enough.
  • LogicConnectors: keeps an object on the server side; needed for progress bar; eg. Report Calculation
  • ServerLookups: used for static methods; no object is held on the server (stateless); eg. Verify Partner in ExperimentingClient; UserDefaults uses ServerLookups internally
    • WebConnectors: very similar to ServerLookup (stateless); easy generation of interface and instantiator, and winforms
  • Cacheable: also static methods; they are not called by the developer, but by the Cachemanager on the server and the client side, when you are accessing a cached table


How to create a new Serverlookup

Depending on its namespace and for server lookups you can use:

  1. Ict.Petra.Server.MPartner.Partner.ServerLookups

nant will collect all "public static <functionName />(<parameterList />)" functions and include their headers into the automatic generated file "csharp\ICT\Petra\Shared\lib\Interfaces\Partner.Interfaces.cs" and "csharp\ICT\Petra\Server\lib\MPartner\Instantiator.AutoHierarchy.cs".

So on the server side you have to

  • use a valid namespace
  • "public static" to define the function

If you want to call this function by the client you have to use:

  1. TRemote.MPartner.Partner.ServerLoookups.<functionName />(<parameterList />)

(The namespaces on the server and the client are mapped one-to-one)

code generation

  • the code for interfaces and instantiators is generated
  • run nant generateGlue for the first time
  • to add new modules (on the level of MFinance, MCommon, etc), search for REMOTINGURL_IDENTIFIER_MCOMMON in ICT/Petra/Shared/Constants.cs and ICT/Petra/Server/app/Core/ClientAppDomainConnector.cs and create similar lines of code for the new module
  • then you can create a UIConnector .cs file; the constructor will be added to the Instantiator and to the interface the next time you run nant generateGlue
  • UIConnector classes are inside a namespace ending with UIConnectors, and implementing an interface, which will be autogenerated by nant generateGlue. The class name should end with UIConnector
  • a method that should not be added to the interface, should either be private, or have the attribute [NoRemoting]
  • Web connector methods are the easiest - they will be added to the interface and the instantiator, and even the winforms: just make sure to add your method to the Webconnector class; run nant generateGlue, it will create the interface and instantiator for you
    • nant generateWinforms will add functions to edit windows, eg. for retrieving data and saving data, using webconnectors
  • WebConnector files are identified by their namespace ending with WebConnectors, and the class name ends in WebConnector. There is no interface, because the methods are static, and that won't work for interface methods.
  • Namespaces in auto-generated *.Interfaces-generated.cs files: Any Namespace that is needed for the signature (e.g. for the Return Value or the Arguments) of a public Method of a WebConnector or UIConnector and that isn't listed in the file \inc\template\src\ClientServerGlue\Interface.cs in the code section above {#USINGNAMESPACES} needs to be manually added to the file \csharp\ICT\Petra\Shared\lib\Interfaces\InterfacesUsingNamespaces.yml! (You'll see the pattern on how to do this once you open the file.)

todo

  • Todo: something special about UIConnectors; extra things are generated for UIConnectors
  • the others are implemented manually at the moment


  • TODO: progress bar; need different threads; keep the connection; prevent the object from being garbage collected
    • need extra object on server for progress and cancel option (eg FReportingGenerator.AsyncExecProgress)
    • keep the main object (eg. FReportingGenerator) alive by registering it
  • Table Maintance: use UIConnector

Data Processing in an n-tier architecture

  • example 1: create a new partner: new partner key is generated, will never be reused, even if the new partner is never saved to the database
  • example 2: create a new GL batch; it is important to have consecutive batch numbers; cancel batch if it is not needed anymore; batch is stored with status "cancelled"
  • example 3: AP Document is created with negative AP number, proper AP number is only assigned on submitting changes
  • options: create new row on server, or on client
  • options: for complex screens with several tabs, store the whole Typed Data Set, or each tab on its own?
  • names that the generateWinforms is looking for in the server: Create<TableName>, Find<TableName>, Save<TableName>, Save<TypedDataSet>