How to code/design the Model part in Caliburn.Micro?

1.2k Views Asked by At

I am new to MVVM pattern and Caliburn.Micro. I've read some tutorials on how to get started, but I'm confused about the Model part of MVVM in the context of Caliburn.

I want to create my first MVVM application and I have some design questions:

  • In tutorials, the Model was presented as simple property in ViewModel. How should I manage more complex models? Is there any naming convention? Obviously, there should be some external classes made for my models, but how should I communicate between my models and the view?
  • How should I keep references to many instances of one complex model? For ex. cumtomers (instances of Customer model class)

  • Is there a possibility to manipulate one model class in many ViewModels? How should I store my model reference, so it'll be visible from different ViewModels?

  • Where should I put my code for more complex model manupulation/file, database storage? How should I invoke such code? I'm not asking here about SQLConnections, but MVVM best practices. :)

Thanks in advance for any help :)

EDIT:-------------------------------------------------------

Thank you for your anwser. I uderstand the topic more clearly, but I'm still confused about some details.

For an example, let's assume this little application. I have a form that allows me to add a new Customer. It has a few fields like Name, Surname etc. After pressing the button, I invoke the addCustomer command in the ViewModel. I want my program to store the newly created customer inside the database.

My view also has the List control (whatever), which displays my customers as raw strings (like "Name: John, Surname: Doe, Address: ..." I know it's dumb to make it like this, but i need an example of model manipulation (like .toString()))

For this example, I've created a bunch of stuff to illustrate my vision of that process:

  • fields - it's a set of form fields like Name, Surname etc.
  • customerSet - it's a set of Customer class to store all created customers
  • .addToDatabase(fields) - a method which puts newly created customer to the database
  • .getStrings - a method which prepares a set of strings to be displayed by the list in CustomerView

I think about 2 approaches that would be good for a solution:

  • First approach. I don't like this one. The only advantage is, that ViewModel handles all the logic inside application. Sharing model would be a serious problem here, because saving methods are bound to the ViewModel class.

First Approach

  • Second, MVC like approach. To me it's the most intuitive one. But - I don't know where should I store CustomersModel object, so few ViewModels could have access to it.

Second aproach

Which is the better one? Or maybe another approach that is more suitable for MVVM?

Another problem is: Where should I put my method that will load all the Customers from the database, so they could be displayes on the list? In "get method" inside viewmodel, or inside a model class?

1

There are 1 best solutions below

3
On BEST ANSWER

In tutorials, the Model was presented as simple property in ViewModel. How should I manage more complex models? Is there any naming convention? Obviously, there should be some external classes made for my models, but how should I communicate between my models and the view?

Your models should represent whatever it is they need to whether it's a customer, account, etc. The view models job is to handle the interaction between the view and models.

How should I keep references to many instances of one complex model? For ex. cumtomers (instances of Customer model class)

Generally, you will map complex models to more friendly format for display, you can do it manually or use a tool like AutoMapper.

Is there a possibility to manipulate one model class in many ViewModels? How should I store my model reference, so it'll be visible from different ViewModels?

If you're working with a local db you can pass IDs around. If it's a service you could persist the model locally for other view models to work with. You could also inject a singleton, ISharedData, into view models that need to work with shared data.

Where should I put my code for more complex model manupulation/file, database storage? How should I invoke such code? I'm not asking here about SQLConnections, but MVVM best practices. :)

Create services for more complex model manipulation / business logic. Inject the services into view models that require them. ICustomerService, IAccountService, etc.

EDIT:-------------------------------------------------------

You're first approach is correct. To your point about sharing the model being a serious problem because saving methods are bound to the view model class. The view model will have a SaveCustomerCommand that is fired when the button is clicked, because of its binding.

The SaveCustomerCommand will persist the CustomerModel, regardless of how the CustomerModel is persisted. So if its a database, the view model might have a reference to a context and issue a _db.Save(CustomerModel). If another view model needs to manipulate a CustomerModel, it will do so by using the context. The view model could also have a reference to a CustomerService that handles the crud for the CustomerModel.

Here's how this might look:

public class AddCustomerViewModel : Screen
{
    private readonly ICustomerService _customerService;

    public AddCustomerViewModel(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    //If button is named x:Name="SaveCustomer" CM will 
    //bind it by convention to this method
    public void SaveCustomer(Customer customer)
    {
        _customerService.Save(customer);
    }
}

public class CustomerListViewModel : Screen
{
    private readonly ICustomerService _customerService;
    private List<CustomerDisplayModel> _customers;

    public CustomerListViewModel(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    public List<CustomerDisplayModel> Customers
    {
        get { return _customers; }
        set
        {
            _customers = value;
            NotifyOfPropertyChange();
        }
    }

    //only fires once, unlike OnActivate()
    protected override void OnInitialize()
    {
        var customers = _customerService.LoadAllCustomers();

        //could just use the model but this shows how one might map from
        //the domain model to a display model, AutoMapper could be used for this
        Customers = customers.Select(c => new CustomerDisplayModel(c)).ToList();
    }
}

public interface ICustomerService
{
    List<Customer> LoadAllCustomers();
    void Save(Customer customer);
}

//same as button, Label named x:Name="CustomerName" will bind
// to CustomerName
public class CustomerDisplayModel
{
    private readonly Customer _customer;

    public CustomerDisplayModel(Customer customer)
    {
        _customer = customer;
    }

    public string CustomerName
    {
        get { return _customer.Name; }
        set { _customer.Name = value; }
    }

    public string Surname
    {
        get { return _customer.Surname; }
        set { _customer.Surname = value; }
    }

    public string Address
    {
        get { return _customer.Address; }
        set { _customer.Address = value; }
    }
}

public class Customer
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Surname { get; set; }

    public string Address { get; set; }
}