4 ms·
One of several great motivations for that approach is that you want the power to refactor the database later without being limited by the fact that you have sev
by cturner 16y ago
One of several great motivations for that approach is that you want the power to refactor the database later without being limited by the fact that you have seven applications from seven different divisions of the company all hooked up to a single schema design. You want to be able to moderate through a business logic layer.
Many businesses wind up in this situation by chance, and then have a very painful journey out of the casual schema they created when they were a small business into something which matches their larger business.
Unfortunately, IMO, supplying a good answer to that question is the 'northwest passage' of software development.
I've spent years at it on and off. I can create such a layer, but the solutions I've come up with are unwieldy from the perspective of the business layer programmer, which is an inadequate solution.
--
If I have another attempt, I'll try to create something which would look to the observer quite a lot like visual basic, but where instead of constructing a Windows forms, you're constructing a state, which consists of a set of forms and a variable store.
The datatypes for the fields would be something like this:
Readonly
Boolean
Text (untyped)
SingleSelect (conceptually like html dropdown)
MultiSelect (like checkbox)
ExposeObject (the object is tagged, client
can bookmark these these objects in its
session)
ReferenceSocket (the socket indicates the tags it
accepts, exposes compatible tags, connection point
for bookmarked objects with compatible tags)
Link (like html link)
Action (like html submit button)
A client API then migrates this with a query language that navigates the states. An example of a dumb session that knows nothing of the app to which it's connecting, and which is rendering a UI on the fly:
# Remote application knows nothing
list all
# It gets back a description of the current state, 'login'
# Remote application logs in to session
assert form is login
set username wizard_2
set password whatever
action login
list all
# Remote application has control again
# Remote application wants to know what operations are available
assert form is portal
list actions
# Remote application decides it wants to go to person
assert form is portal
link "manage person"
list actions
# Remote app decides to add a person
assert form is "manage person"
link new
list all
# Shows fields and actions
assert mode is new
set firstname Wiz
set lastname Ard
set age 40
select male from singleselect
action save
# Application throws an exception with details
# Client app
assert mode is new
set data_of_birth 08081971
action save
list form
Just as SQL is a flexible language for a relational model, this language is a flexible language for an interactive state graph. A client progrmamer who know the state mapping of the app could write tighter instructions than this.
That client app can be anything: desktop app, unit test framework, web app, direct rpc over http.
Conceptually the situation of states, objects and actions is very similar to a text adventure, but I'd been working on it for years before I realised this.
Every time the state progresses from one to the next, it keeps a copy of the previous state in memory.
This logic layer has the database underneath it. The DB is now abstracted away from the user-facing application. Major benefits of this approach:
- You can design the application with almost no concern to user interface. Then you can create a streamlined user interface later that leverages the correct business logic. You can separate the costing for both projects. That's how I came to the problem. It would revolutionise software consulting.
- You can wrap a dumb client interface around the engine. Want your entire business logic exposed to the web, securely? Or RPC? Or mobile? Or for the blind? Done, with literally zero extra effort.
- If we had a relational database that had version numbers for its internal state (think git model), you could write applications that could unwind and rewind. Takes a lot of memory, but you could store the states in a nosql database.
--
OK. I'll try to answer your question now with the best I know of current techniques. Things using current technology that somewhat address the question you asked:
- Filter all chat with the database through stored procedures. Problems with this: (1) non-stateful, you don't have the user's application session; (2) stored procedure languages are a pain to work with. This is the sort of solution you'll find most often in large organisations because you can hire a single known skillset (DBA) who knows both stored procedures and schemas. It's awful but it works.
- Put a HTTP layer between the database and the application. Do all interaction with the database through this. In the payload that users send in they could (1) name the action they want to do such as create person and (2) supply the arguments they should send in for that business case. Basically, here you're doing stored procedures but in language of your choice. Latency is higher than with real stored procedures.
- Have a rule that all application programmers integrate with the application using Apache Cayenne or Hibernate or something similar as an object-relational modeler, in a strongly-typed language. Insist on use of Data Access Object pattern (see Fowler) for any original entrypoint to the object graph. Now when you refactor your database, regenerate your schema, and use the static typing checking in Java to rapidly adjust your application to the changed schema. You'd need to get all client apps to do this, but it's a lot more rapid than if you've got application users using SQL.
- cturner 16y agoCorrection. Not that I expect anyone will ever read this now the thread is done in HN, but I like to get it right. I sited Fowler, thinking _Patterns of Enterprise Application Architecture_. I've checked and was mistaken. He does somewat similar stuff in QueryObject, but I'm thinking of a much higher-level concept. I'll desccribe the relevant ORM patterns that I'd use. Most of my experience is with Apache Cayenne (which I strongly recommend), and some with Webobjects EOF before that. This has been interesting to review. I haven't done serious work in either a compiled language or application development for years. It's interesting to come back at with fresh eyes. = Entity Your ORM will have tooling that will generate these classes for you from a tool or definition document. There is a class per table in the schema. Each instance of Entity corresponds to a row of data in a table. Methods for foreign keys return objects for the linked class. Your application would have classes Person, Organisation, etc. ent.Person would have a method ((ent.Organistion) toOrganisationEntity()). end.Organistation would have a method ((List<ent.Person>) listPersonEntity()). Once you have an entity, the app can find related things via lookup. Say you had the Organisation object, and wanted to know all the subjects that people for this company were enrolled in. You bounce through the graph. From Organisation you can get to Person, from there to JoinPersonSubject, from there to Subject. An improvement on this is to have a separate base and application class. Your ORM generates the base class. You generate your app class and have it extend the base class. You put custom logic in the application class. Example: Cayenne generates com.company.app.ent.base.Person. It has methods corresponding to each column in the person table. You create com.company.app.ent.Person (different package name) and have this extend the base class. You put convenience methods like ((String) getFullname()) in there. You can then easily regenerate the base class without impacting your custom logic. = DataAccessObject (The summary sentence doesn't make much sense until you understand the concept, so I'll explain with example immediately afterwarss.) Summary: You use a DataAccessObject for getting entrypoints into the graph of data held in the database. Explanation: In Entity I described an example of bouncing around the graph. Say you had a user in an education system, and they wanted to get a list of all the organistaions associated with their office. You'd use a DAO to do this lookup. Strangely, these patterns make a powerful coombination. You'd think that you'd need to add a bunch of extern Your ORM probably won't generate these, but you should abstract them away into distinct classes like this rather than putting static methods in your DAO functionality is commonly bolted into Entity classes as an afterthought using static methods. Ugly. Awful. Instead, consider DAO to be a distinct pattern, extending from an abstract base classs. The base class constructor will accept the current user as an argument. There will be a method that forms an ORM join between the current user and the the current user you'd have common functionality such as a join between the Unlike Entity, there is not a one-to-one mapping between data access objects and tables. You just create them as you need them. There might be an OrganisationDAO that has the query above, and some other queries in it. But then there might be a situation where you're doing a strange mega query across Organisation, Location and SalesTarget that doesn't seem to be specifically associated with any of those tables more than another. Here, you just create a new DAO. = Session The user's application session. In this you'll have a single instance of each DataAccessObject. That way your application can do lookups by going session().orgDao().listOrgWithAccountsAtUserOffice().