Concepts - Stateless persistence
Preface
A remark on stateless development is in order, because a lot of developers misunderstand what 'state' really means
in the n-tier context. By convention, all n-tier applications should be 'stateless'. This means that the total application
is not in some kind of 'state' by holding information in memory, or better: the separate tiers shouldn't be in a certain
'state' where they hold information in memory. The 'state' of the application is held by the database(s) used. This model
is completely the opposite of the stateful way the so called 'fat'-clients used to work: the user worked with the
complete application on his machine, and the application's state was held in memory.
The reason for a stateless approach is quite simple: multi-user. In a fat client, single user system, the application state
was the same as the user's state, and thus it was a great performance gain to keep that user-state in memory instead of
the database. For a set of black-boxes stacked on top of each other, serving more than one user, the application state
is not the same as the user's state, in fact, for every user, the application can act completely different. In these
circumstances, in the n-tier application world, a stateless approach is more convenient, since the logic can assume correctly
what the current state of the application is (which is stored in the database) and can use other system parts, like COM+
and MTS or even webservices, without having to negotiate a current 'state'. Also, the reuse of system resources is very
flexible in a stateless environment: as soon as a given object is done using another object, it is destroyed (normal
objects) or returned to a pool (for example connection objects), and not held in some global store. When thousands of
users are using the system, it still will perform well, because it doesn't keep thousands of objects in a global object
store but shares a smaller group of objects among the ones who need them.
Stateless development and O/R Mapping
If you work with objects that hold the data of an entity, the term 'stateless' becomes a little blurred and you can't
really talk about a pure stateless environment anymore: holding any entity in memory practically makes the application
stateful. Still, LLBLGen Pro will not
cache the in-memory objects, and therefore considers the objects in memory
as
temporary mirrors of the entities in the persistent storage. Every action on entities in memory has to be
considered as 'stateless actions', and should be persisted a.s.a.p. As a user of the generated code, you should not cache
loaded objects in memory longer than necessary, because changes made by other threads, on other machines, to the entities
in the persistent storage are not seen by your thread and therefore your code can wrongly assume the entity data
held by the cached object is valid, however it is out-of-sync with the
real entity in the persistent storage.
In the generated code you will find entity collection classes. These collection classes have methods to work with
more than one entity and work
directly on the persistent storage (using Adapter, you'll find these methods in the DataAccessAdapter class).
This means that when you update a set of entities through a method on an entity collection object, you are actually modifying the real entity data, which is
seen by all other threads and users in your application. This way you can be sure the changes are propagated to other
threads in your application as well. For individual entities and objects holding their data: create the object, read
the entity data into the object, use the data, and get rid of the entity object right after that. LLBLGen Pro doesn't
use database locks on table rows to prevent some sort of concurrency control; every entity object is disconnected and
can move around in the application freely. The application should be aware of the fact that when data is altered in
an entity object, it has to propagate those changes a.s.a.p. back to the database.
There is no need for panic though. In most applications there will be no problem at all, and most concurrency control
problems which are related to the in-memory caching problem, can be circumvented by a variety of solutions: be it
optimistic concurrency control, functional locking (locking of functionality for other users if a single user is
using a harmful piece of functionality) etc.
User state
User state is a confusing term, because in a way, it
is a form of stateful programming. User state is the
state of the application for a typical user, but only in such a way that the user
thinks the application
is in his state, most of the time meaning just GUI related data is kept in a stateful global store, typically
in session related objects, which are common in the ASP and ASP.NET world. ASP developers know as a rule of thumb
not to keep large objects in the 'session' object, like recordsets or open database connections. The reason for this
is that the application suddenly becomes stateful even if the developer didn't intend this. In the .NET world, this
hasn't changed. User state, for example the information a user has provided on page one and two on a four page wizard,
is light and influences only the GUI tiers. Data meant for other tiers shouldn't be kept in the user state but should
be stored in the database. As a rule of thumb use this: "if tier N (N being not the GUI layer) is changed into a
stateless webservice, placed on a webserver on the internet, thus not in your local secure network, will the
application still work as planned or do you have to change a lot in the application to keep it together?"
When you have to change a lot, your application isn't stateless, when you don't have to change a lot, your application
is pretty 'stateless' and you're well on track!