Layering of Transactional Applications, and handling of state

Case being considered:
A single database is in use, by multiple users, possibly concurrently.  The distributed case of multiple databases in use  is much more difficult and should be avoided if reasonably possible. The use of O-R Mapping is equally valid for the distributed case, but the full system is more complicated, so for simplicity, not treated here.

Basic Rules
1. All persistent state resides in the database. The database is the expert at handling shared data safely in face of concurrent access, so we use it fully. Per-user data (which is unshared) may live outside the database.
2. All interactions with the database data, even read-only access, are brought about through actions inside transactions.
3. No user interaction (necessarily causing long delay) may occur during a transaction because a transaction should be atomic, or nearly so, and the database may hold locks to make this true, or if it doesn't, the lack of atomicity gets worse and worse the longer the transaction lasts..

To make it easier to obey rule 2, the transaction code is usually organized into methods, so that each transaction runs completely inside a call to some particular method. The transactional methods are grouped together to make the service layer. The collection of method signatures of the transactional methods defines the Service API. The user interaction code makes up the presentation layer, which calls into the service layer as needed to carry out the actions of the app.  We think of the Presentation layer as above the Service layer, separated by the Service API:

Presentation layer: line-oriented, GUI, or web scripts
---------------Service API-------------------------
Service layer: methods with transaction(s) within them.

Thus UI actions and their huge delays occur strictly in the presentation layer between the calls into the service layer. Here is a timeline for one user's execution: starting in presentation layer, calling into service layer, returning to presentation layer:
 
----UI-------<call service>--<start Tx>----work with database data----<end Tx>--<return from service>--UI-----

The service API defines what the app can do for any presentation layer, whether it's a console (line-oriented) app, a local GUI app, a client-server app, or a web app. One service layer implementation can support many presentation layers without change except to initial configuration code and/or files.

Domain objects: changes are done in service layer, treated as read-only in presentation layer
In general, all changes to domain objects should be done in the service layer, in a transaction, since their data is persistent and "belongs" to the database.  Once domain objects are finalized in the service layer they can be sent up to the presentation layer for read-only use there. This is the conservative approach that will work in any O-R mapping and is used in the pizza app. The current Topping and PizzaSize objects are supplied via service-layer calls for the order-pizza form, and the ids of the toppings and size picked by the user are assembled in the presentation layer and the "makeOrder()" service is called with them as arguments. In the service layer, the order object is created and saved. Alternatively, you could imagine creating a new order object in the presentation layer as a "scratch" copy, not immediately persistent, filling it in with referenced Topping and PizzaSize objects (also now just scratch copies, since we are outside of a transaction here in the presentation layer) and then sending it down to the service layer in a makeOrder(PizzaOrder) call. However, now the scratch objects need to be converted to persistent objects, a more complicated and error-prone operation than simply creating a new one under a transaction. To keep things simple, the conservative approach is used everywhere in the pizza app.

In the supplied example, the service layer API is provided as a singleton stateless object in both Hibernate and EDM implementations, with essentially the same methods in both cases. Since transactions run strictly inside service-layer calls, the whole transactional state is created and destroyed within a call, so nothing needs to be saved from service call to service call for transactions. All shared application data is held in the database, and none of it is in the service layer between transactions.  This strategy is called "session-per-request" strategy in Hibernate language, referring to web requests, but generalizable to service requests between UI actions in any app.

Per-User Data
The presentation layer may hold some data related to the current user running the app across several calls to the service API.. In the pizza app, the user has a room number for pizza delivery that is remembered in the presentation layer across various calls into the service layer to order a pizza, check if it's done yet, etc. This is unshared data that does not need to be held in the database. If the computer running the app crashes, the current room number is lost for the user, except in web app cases where the application server persists session data. If more persistence is needed, per-user data can be kept in the database as well as shared data.

The Data Access Object (DAO) Layer
As discussed above, the crucial layer boundary is between the presentation and service layers, to subdivide the work into transaction units that run between the much longer user interactions. A less important layer can be set up below the service layer to encapsulate the domain object access needs of the application. This allows substitution of different O-R mapping implementations or file-based persistence for some uses. The pizza project has a DAO layer, with olmost identical methods in both implementations.

Home