Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Virtual Interfaces

Virtual Interfaces

Presented at Coolblue's Behine the Scenes Delphi meet-up

87a3c5c9340596a46980777d4ff71ad7?s=128

Michał Kulczycki

November 29, 2018
Tweet

Transcript

  1. Michał Kulczycki | Senior Software Developer | michal.kulczycki@coolblue.nl | 2018-11-29

  2. Agenda Things I hope to cover: • Vanessa: Coolblue’s ERP

    • Interfaces of DB components • Virtual interface as a generic implementation • Examples and real code • Tips and tricks (and warnings)
  3. None
  4. No easy wins • Millions of lines of code •

    Developed over last 15 years • Roughly 2000 functions • 90% with their own data-modules • Each containing 2 - 70 DB components • No unit tests
  5. Data modules: state 1. Client datasets and lookups (for DB-aware

    controls), 2. Various utility procedures and view properties, 3. Lots of inherited functionality for the view VIEW DB DATAMODULE
  6. Data modules: DB access 1. Customised ADO queries, commands, stored

    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
  7. Data modules: business logic 1. Usually tied to state (e.g.

    selected rows) 2. Resulting in direct DB access 3. Mixed with APIs, some procedural code and other DMs VIEW DB DATAMODULE
  8. Logic: stateless & tested use-cases DB LOGIC API VIEW DATAMODULE

  9. Logic: further abstractions injected DB LOGIC API SERVICE 2 SERVICE

    3 SERVICE 1 API DB
  10. Logic: free of implementation details DB LOGIC API VIEW DATAMODULE

  11. Solution: a humble wishlist • Eventually replaceable by APIs and

    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:
  12. None
  13. SQL statement: INSERT instead of: given interface: we can just:

  14. SQL statement: DELETE instead of: given interface: we can just:

  15. SQL statement: a scalar SELECT instead of: given interface: we

    can just:
  16. SQL statement: a complex SELECT instead of: given type and

    interface: we can just:
  17. SQL statement: a complex, multirow SELECT instead of: given type

    and interface: we can just:
  18. SQL: in DFMs

  19. SQL: in external files or resources

  20. One small problem Implementation of some specific interface is easy.

    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...
  21. (of multiple, unknown interfaces in one class)

  22. None
  23. Virtual interfaces are a thing! Virtual interface is: • an

    actual Delphi library class called TVirtualInterface • descending from TInterfacedObject (ref-counted) • living somewhat forgotten life in System.Rtti
  24. Virtual interface’s expectations TVirtualInterface instance is created with two unusual

    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!
  25. Virtual interface’s birth It’s whole purpose in life is to

    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.
  26. How is that useful? • Class does not have to

    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.
  27. Let’s see that in action

  28. What about APIs? • Initially generated from our OpenAPI Delphi

    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
  29. Let’s see some examples

  30. None
  31. RTTI must be requested RTTI information is crucial but Delphi

    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.
  32. It walks like a duck, but isn’t one • Virtual

    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:
  33. They’ll never know it was a duck

  34. Careful with generics • Generic bloat has been reduced but

    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.
  35. Separate type inspection from execution RTTI data inspection during an

    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.
  36. Self comes first • Args[0] is not first argument of

    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
  37. Michał Kulczycki | Senior Software Developer | michal.kulczycki@coolblue.nl | 2018-11-29