N-tier architecture: Difference between revisions
Jump to navigation
Jump to search
(34 intermediate revisions by 5 users not shown) | |||
Line 23: | Line 23: | ||
** advantage: server knows about the client (stateful); minimum data transfer, diffs are enough. | ** 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 | ||
* | * 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 == | == code generation == | ||
* the code for interfaces and instantiators is generated | * 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 | |||
* to add new modules (on the level of MFinance, MCommon, etc), | |||
* 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 | * 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 | ||
** a method that should not be added to the interface, should either be private, or have | * 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 | * 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 == | ||
* Todo: something special about UIConnectors; extra things are generated for UIConnectors | * Todo: something special about UIConnectors; extra things are generated for UIConnectors | ||
Line 50: | Line 73: | ||
* 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 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 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: 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? | * 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> | * 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:
- 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
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>