procedures etc., 2. Using common objects shared by hundreds of DMs, 3. Including connection object from main form’s DM (globals, yay) VIEW DB DATAMODULE
domain objects • Zero DB types exposed (incl. TDataset & TField) • SQL moved from DFMs to … somewhere (and kept static) • Supporting all types of DB objects and DDL/DML operations • Independent of client/driver (e.g. ODAC instead of ADO) • Making logic fully unit-testable (thanks to total abstraction) We wanted something:
Multiple implementations also pose no problem. But all boils down to something like: An interface requires some implementation. And we did not want to write thousands of those...
parameters: 1. RTTI’s type information (PTypeInfo) about some interface, 2. OnInvoke event handler it will call when invoked: .. and that is pretty much it!
do two things only: 1. Pretend that it actually is an implementation of that interface, 2. While transparently forwarding all its method calls to OnInvoke event.
be declared as an implementation of an interface • Handler (implementation) has to know how to adjust its behaviour based on: • RTTI information (type description: names, types, attributes etc.) • arguments (just like any other method accepting those). So, if we can fit all control information into type declaration and normal arguments, we are going to get ourselves a generic implementation of some kind of interfaces. Like any and all SQL statements.
code generator • Generated code is quite simplistic and rigid • But: implementation is repeatable • Most complex DTOs can be easily marshalled into JSON • Special cases can be handled with attributes. SERVICE 2 API DB
won’t generate it unless your interface: 1. inherits from IInvokable: 2. or is preceded with {$M+}: Fun fact: First one works 99.9% of the time. Until it doesn't.
interface instance acts like an interface. Runtime. • You can cast an instance, but you need one first. • Delphi (and Spring container) will rebel against it:
still is significant. • Sizeable implementations will bloat your binaries (biggest Vanessa DCU: registration of 1 API + 8 statements), • Generics are useless during type inspection: you’ll do TypeInfo(T) and proceed with PTypeInfo anyway • Use generic stub to: 1) Do that cast and, 2) Pass resulting PTypeInfo to an actual implementation.
Invoke is wrong on many levels: • Will be executed multiple time (RTTI is fast enough, but still…), • Often will be too late: context can depend on previous call, • It is a bit too late to learn that something is missing or wrong, Instead: • Inspect type once, preferably before instantiation, • Raise inspection errors, log warnings and information, • Cache results in some structure and just serve it, • Every new instance will be fully operational instantly, • Bonus: they won’t need generics and maybe even PTypeInfo.
the Method invoke (It will be in Args[1] if there were any (of course) • Args[0] is always equal to Self ( in TValue) • Failure to rember that will result in craziest of run-time errors