TMS Aurelius and MVVM design: an example (Part 2)

30 Responses

  1. Didier says:

    Generally speaking, I”m afraid that the MVVM way of coding doesn”t meet the “keep it simple” philosophy permitted by a Delphi “Rapid Application Developpement” : simply put components on a form and connect them to data.

    Please John, forgive me but from my little point of view, i found that the purposed sample shows “how to make thing complex when you can make them easier”… The VIEW connection is awfull: you have to list in a record all the visible controls in order to enanble/disable them according to the state of data operation! A simple TDataSource connected to a TDataSet just do it fine, isn’it? Why do we have to reinvent the wheel?

    In the same time i think it”s important to decouple code (logic/UI) for many of the goods reasons exposed (test, logic, reusability, platform translation, sharing..).

    So i will recommand, when creating a new project, to begin to write the “core logic” in a consol application. Why not create in this phase a global object/component that will gather the main objects, procedures and functions (primitives). TMS AURELIUS is good at this level!

    In a second time, decide the UI platform VCL/FIREMONKEY/DLL . TMS Aurelius is (very) good at this level too with its DataSet possibility (i.e coupling data with UI elements with some DataBinding or TDBEdit and so on…)

    I”ve not clearly understood associations after 3 years using TMS AURELIUS. I use TMS Data Modeler to get the right coding but don”t know precisely how it works…

    • John Kouraklis says:

      Let me clarify something which may not be apparent at first place in the posts; this example attempts to show how you can use Aurelius in a MVVM design. So, the focus is not on ideal MVVM design but how you can isolate Aurelius and, still, be able to move back and forth in the specific design paradigm information which is governed and determined by the framework.

      Having said this, please see a few thoughts on your comments:

      Generally speaking, I”m afraid that the MVVM way of coding doesn”t meet the “keep it simple” philosophy permitted by a Delphi “Rapid Application Developpement” : simply put components on a form and connect them to data.
      Please John, forgive me but from my little point of view, i found that the purposed sample shows “how to make thing complex when you can make them easier”…

      This is a very simple example and, arguably, separating the components can introduce more coding, properties, etc. The value of such separation is coming into play with bigger projects. Anyway, this is a very common criticism and debate among MVVM zealots and sceptics.

      The VIEW connection is awfull: you have to list in a record all the visible controls in order to enable/disable them according to the state of data operation! A simple TDataSource connected to a TDataSet just do it fine, isn’it? Why do we have to reinvent the wheel?

      As said above, it is a simple example. There are more innovative ways to do all this housekeeping but I, just, used properties for the sake of the example as the focus is more on Aurelius. Moreover, in the approach here, I don’t use any graphical data bindings (LiveBindings, etc.)

      In the same time i think it”s important to decouple code (logic/UI) for many of the goods reasons exposed (test, logic, reusability, platform translation, sharing..).

      Exactly; but this berries some associated cost and your observation about the properties is one such a cost.

      So i will recommend, when creating a new project, to begin to write the “core logic” in a console application.

      Just take the model(s) and put them in a console.

      Why not create in this phase a global object/component that will gather the main objects, procedures and functions (primitives). TMS AURELIUS is good at this level!

      What are the “main objects” you are referring to? The model(s)?

      In a second time, decide the UI platform VCL/FIREMONKEY/DLL.

      I have; this is a FireMonkey application.

      TMS Aurelius is (very) good at this level too with its DataSet possibility (i.e coupling data with UI elements with some DataBinding or TDBEdit and so on…)

      Yes; with TAurliusDataSet. But, again, in an isolated design, the View should never know about the whereabouts of Aurelius. What a View knows is how to retrieve information from the ViewModel, which, in turn, represents data and graphical states.

      I”ve not clearly understood associations after 3 years using TMS AURELIUS. I use TMS Data Modeler to get the right coding but don”t know precisely how it works…

      I use TMS Data Modeler as well but for a different reason. 🙂

      • Didier says:

        “Merci” for your return John.
        I’m developping since 1984 beginning with Macintosh ASM 6800, Think Pascal and Think C++: I had the time to see a lot of modes in programming…. It’s hard to convince old apes like me who have taken bad habits. But there is always something good to take new theoretical steps, especially when the concepts are well explained as you do.
        Désolé pour mon anglais!

        • John Kouraklis says:

          Thanks Didier.

          I can understand your point about the challenges to change. I have found myself in this situation in the past.

          But, as you mention, as well, theoretical discussions and new approaches are always welcome. This is how things move in the long run.

        • Dave Boltman says:

          Didier – I also started programming 1983 on a Motorola 6800 D2 development kit. I think we have the same bad habits to break 🙂

          Regards,
          Dave

  2. Wagner Freitas says:

    Tank´s
    Very Well

  3. Wagner Freitas says:

    I want more samples and thecnics;
    But great sample

  4. Wagner Freitas says:

    I reading your book,
    I am studing

  5. quanliking says:

    Thanks John.
    I am looking for MVP and MVVM in delphi for some days, your book and papers on MVVM in Delphi and Aurelius are fantastic, and I try to transfer your MVVM code to uniGUI Framework and it works well.
    Good jobs.

    • John Kouraklis says:

      Thanks for the comment.

      Let me know if you need any assistance. I am more than happy to help.

  6. João de Oliveira says:

    Hello John, Congratulations on your explanation, but would you like to make a small example without using MVC, just to know how to create a firebird database using firedac with TMS Aurelius?

  7. Piotr Murawski says:

    Are You shure that this code is correct?
    procedure TModelMain.getCustomer(const aID: integer; var aCustomer: TCustomers);
    begin
    aCustomer:=fObjectManager.Find(aId);
    end;

    I think it should be like that
    procedure TModelMain.getCustomer(const aID: integer; var aCustomer: TCustomers);
    begin
    aCustomer:=fObjectManager.Find(aId);
    end;

    and similary when You use “Find” method.

  8. Piotr Murawski says:

    The difference is that the ObjectManager should be know what kind of object do You expect as a result of Find method. You usually have more that one entity in the database and finally many classes in code. As You know in the bracket is a value of the primary key but which class (entity)?

  9. Piotr Murawski says:

    Ups 🙂 I make mistake in the sugested code 🙂 should be like that
    procedure TModelMain.getCustomer(const aID: integer; var aCustomer: TCustomers);
    begin
    aCustomer:=fObjectManager.Find(aId);
    end;

    • John Kouraklis says:

      Hi Piotr,
      it looks like you miss the in your code as well 🙂

      You are right…the generic type is required.

      Funny enough, if you look at the original code on github, the statement is correct.

      Thanks for pointing it out to me. I have now corrected the code in the post.

  10. Piotr Murawski says:

    Hi, have send two times the same code with the class name … I think that the name of the class could be interpreter by mistake as a HTML tag in this why is removed :).
    All the best 🙂

  11. Osiel Gomes says:

    In this approach if I use the TAureliusDataSet would it be in the ViewModel layer?

    I would put TAureliusDataSet in the View (Form) and I would provide the Criteria or Source List from the ViewModel.

    If adsCustomers is the TAureliusDataSet in the View, then I would write something like this:
    adsCustomer.SetSourceList(fViewModel.getFullCustomerList)

    And in the case when the ViewModel layer is freed from memory by the ACR if using the view interface would not accuse Access Violation?

    Not sure I understand what you mean here; if you have the dataset in the View (Form), it will be freed normally. If you put it in the ViewModel, you will create it with the owner to be nil and, then, you need to free it manually.

  12. Louis says:

    Great article just one small question. How do you deal with null values when you connect to non-data-aware controls?

    • John Kouraklis says:

      Thanks.
      What do you mean to “non-data-aware” controls? And do you mean programmatically or using TAureliusDataSet?
      Can you provide an example to illustrate the problem with null values?

  13. Louis says:

    Hi John,

    Never mind John, I had some brain fart 🙁

  14. Shane says:

    Hi John

    Firstly, thankyou for taking the time to create this content – I am just in the process of learning MVVM and as an Aurelius user I have found this to be very useful.

    One other thing I am wanting to learn is how to do TDD, and I noted as I was working through your examples the use of interfaces and also the comment that the “Model class is, easily, testable”. I’d be really interested to know just what you would test in the model class, and an example of how to unit test one of the methods would really enhance my understanding of how to use all three technologies (Aurelius, MVVM and TDD).

    • John Kouraklis says:

      Hi Shane,

      Apologies, I missed your comment. Thanks for the kind words.

      About testing the model, I would create a descendant of IAurelius which links to an in-memory database and pass it to TModelconstructor. After that, the interface methods can be tested

  15. Shane Sturgeon says:

    Hi John,

    Thanks for the pointer re TDD – Once I have mastered MVVM, I’ll be sure to apply that approach.
    Another question if I may, this time about the point you make re the concept of data existing in every layer. I’ve read your book and been working through creating my own MVVM test application and it seems entirely logical to me that you would use the entity classes (e.g. TCustomers) in every layer, yet in your examples, you seem to do a lot of converting between records and objects and I’m not sure why. The case above for example you use a TCustomers object to populate a Transient Record which is then a property of the IViewModelAddEditCustomer Interface.

    Is there some reason why the TCustomers object couldn’t be the property of the interface, and have the view directly update the properties of that object? I don’t know much about interfaces, but I’m wondering if it’s because of a limitation of Interfaces or specifically a MVVM reason?

    If it’s best to use Transient Records for this purpose, I’m thinking that perhaps having an “Assign” like method in the TCustomers (and it’s temporary record partner) class would make some sense – any thoughts on the MVVM validness of that approach?

    • John Kouraklis says:

      Hi Shane,

      Is there some reason why the TCustomers object couldn’t be the property of the interface, and have the view directly update the properties of that object? I don’t know much about interfaces, but I’m wondering if it’s because of a limitation of Interfaces or specifically a MVVM reason?

      You can use TCustomersanywhere and in any layer you want and, yes, it can be a property as you suggest. For the purists, it would be absolutely consistent with the MVVM approach. The only reason I have the transient record in this example is to show that the view model works as a layer in between the model and the view where you can manipulate data before pushing it to the view. You may wish to add more information, perform checks, trigger actions, manipulate what should be shown to the view. All these are things that may not necessarily be done in TCustomer, in the model or in the view.

      I hope this makes sense

      • Shane Sturgeon says:

        Thanks John – that all makes sense. I was wondering if I should include validation etc. in my version of the Tcustomers class or allow the Viewmodel to register as an observer of that object to get notified of changes so it could then process similarly to your example. Guess there is no real right or wrong way to implement this. CheerS.

        • John Kouraklis says:

          Hi Shane,

          I think it has more to do with the structure of the classes and the code and what sort of validation we are talking about. The last version of Aurelius introduced the ability to validate properties and this is done very close to the database level (just before the changes hit the database).

          Other types of validations or checks may work as decision points in the whole flow of an application. I can think of a situation where you need to approve an order of an item or a service. You would need to combine data from the customer database, the inventory, the details of the order, credit checks, payment completion, etc. Under MVVM paradigm, all these seem like separate Models and the checks can be done in the ViewModel as it can host several Models. And, if you err on the side of purists in OOP, then you may have separate classes for readers and writers of all these models. ViewModel seems like a space where you can manage all these under one roof.

          Another type of checks you may find confusing is exceptions. Most of the exceptions need some form of visual notification or acknowledgement that involves a “view”. My personal preference on this is to check for exceptions in the ViewModel and notify the View. This can also take place in the Model but you then need to somehow pass the notification from the ViewModel to the View to show a message. I found this becoming cumbersome and it involves a lot of boilerplate code without real benefits.

          So, as you say, there is not a strict academic right way to do this. 😁😁😁

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.