GncBusiness v. GNCSession

Derek Atkins warlord@MIT.EDU
20 Nov 2001 13:52:46 -0500


Linas,

It feels as if we're talking past each other. :(

Let me see if I can be more clear.

Right now, all the engine is stored as one big glop of code.  I'd
like to modularize it a bit more, in order to add more core objects
to the engine in a dynamic manner.  By "core object" I mean a basic
accounting object, like an Account, a Customer, an Invoice...

More inline..

linas@linas.org (Linas Vepstas) writes:

> > Um, I think you misunderstood what the registration does... It doesn't
> > register an instance of a business object -- it registers the business
> > object "class".  It's a static global, and is done at program
> > initialization time.  
> 
> write now, gnucash doesn't have any structure that holds 'static
> globals', not unless Dave snuck one in there while I wasn't looking.

Yes it does.  Take a look at gnc-module.c

To give a better idea of what I mean, what I want to do is provide a
registration where the core object C-code can register itself with the
engine, so the engine knows that it exists.  There is a structure:

	GncBusinessObject {
		int version;
		char * module_name;
		char * description;
		void (*destroy) ();
		GList * (*get_list) ();
		...
	}

During the gnc-module initialization, the code calls
gncBusinessRegister() which passes a (static) instance of the
GncBusinessObject into the engine to register the object type.

Through this mechanisms, new object types can be entered into the
system without changing any other code.  My "search" widgets, for
example, can choose ANY registered object.  If I create a new object
and register it, viola, I can now search it without any additional
code changes!

> > The second step is an Entity Table, and those
> > belong in the Session.  
> 
> I don't know what an 'entity' is.  The session table is really meant to
> hold network connection things.   Note that if there is no open
> connection to a file or sql (or rpc) backend, there is no session.
> (At least, thats how it used to work...)

See GNCEntityTable in GNCIdP.h

An entity is an instance of an object.  For example, the result of
xaccMallocAccount() is an entity.  The result of gncCustomerCreate()
is an entity.

An Entity Table is how you lookup an entity.  Instead of requiring the
equivalent of xaccAccountLookup() for every new object type, you can
look it up via a general lookup function and pass in the object type
name:

	gpointer gncBusinessLookupGUID (business-session, module_name, guid)

> You can use gnucash without having a session: you just won't be able to
> save your work until you do start a session.  

Can you?  It certainly doesn't look that way to me.  All the GUID
tables are stored within a session.  Without a session, you have no
GUID tables, which means you cannot create any objects.  So I
disbelieve that Gnucash works without a session.

I agree that you don't need to have an open book!

> > Well, this is in lieu of moving the business objects into the engine.
> > I can always do that if you want, but I was hoping to keep it
> > separate.  If you want it all integrated, that's fine with me -- it
> > would make life much easier.
> 
> 'integrated' is a word that has shades of grey.  If you just want a few
> pointers in some struct to dangle your data from, I have no objections.  
> But if you start accessing other engine data members without using the 
> formal API, well, that might not be so good.  

By "integrated" I mean:
	1) Move all of my code into src/engine
	2) Link all of the .o files into libgncmod-engine
	3) Extend the existing engine backend to handle the business object
	4) All other "integration" that would tie the business
	   code into the engine

I don't think either of us really want this.

Besides, when the next person comes along and wants to add yet another
core object, what are they supposed to do?  A real core-object extension
method would be best, IMHO.

> What you understand is not what I meant.  What I meant to say was that 
> you don't have to use the current "struct Backend" to access the data 
> store.  All you really need to do is to provide some vector table so 
> that other people can write methods to access other storage media. 

Right, but who calls/fills in the vector table.  Who calls "save"?
And how does the caller know all the various "save" functions to call
out to?

With a registration method, you can centralize the 'save()' api but
have it call out to any number of "save()" methods.  At least in
theory.

> I do beleive that a storage backend for business objects will probably
> be very tightly integrated with the engine backend.   But that does
> not mean that business object calls have to flow through the current
> 'struct backend' to acheive this.  There's nothing wrong (that I see)
> with design a different 'struct busns_back' for use by the business
> objects; I also se a few advantages for keeping it separate.  The
> actual code underneath 'struct busns_back' might be very tightly
> integrate with the the code underneath 'struct backend', but that
> is an implementation issue, not an architecture issue.

It seems strange to me to have "coupled code" that uses a different
interface.  Besides, it still doesn't easilly allow extension.

Perhaps that isn't a bad thing.  Perhaps it is.  But again, what
happens with the next person to come along?

> > MY question is: how do you extend the backend to
> > know about new objects?
> 
> There are several ways.  The one you seem to want is to add new vectors
> to 'struct backend'.  But that is not the only (or best?) option.  
>
> I'm proposing:
> Add a pointer in GNCSession to point at your business class.  
> Your business class  contain a pointer to a 'struct BusinessBackend'
> which you can define as you wish.   
> 
> Does that make sense?

I understand it, but I'm not convinced it's the best way to go.
It might be the most expedient, however.

If the backends are tightly coupled via implementation, why not
couple them via API?

Simiarly, assume a File backend -- how do you store the "two backend"
data into one file?  Or are you presuming that the current
engine would store data in file 1, and the business backend would
store into file2?

I guess my confusion is that if src/backend/* needs to be changed, why
not just change Backend * at the same time and provide a coherent
Backend interface instead of multiple interfaces to essentially the
same code.  The alternative is to break up the Backend even more such
that each core object (Account's, Transactions, Splits, Customers,
Invoices, etc) have their individual Backend API.

> --linas

-derek

-- 
       Derek Atkins, SB '93 MIT EE, SM '95 MIT Media Laboratory
       Member, MIT Student Information Processing Board  (SIPB)
       URL: http://web.mit.edu/warlord/    PP-ASEL-IA     N1NWH
       warlord@MIT.EDU                        PGP key available