DEV Community

Discussion on: Kentico 12: Design Patterns Part 20 - Choosing a Solution Architecture

Collapse
 
seangwright profile image
Sean G. Wright

Hey Andy!

Thanks for reading and I'm glad the posts have been helpful.

Hmm... that seems like a lot. Does your page type actually have 20-30 fields? This might be a content modeling issue that is leading to a technical one.

Have you looked at AutoMapper? It can help with passing data between layers, especially when the types and property names are pretty well aligned. This allows each layer to own its own types which is important when keeping them loosely coupled.

Another option is to break up the data into groups that get mapped to the view through child actions. Assuming your data access is cached, calling the same DB query APIs multiple times won't cause issues, and you can then map your page type into different DTOs that returned by separate child action calls in the same view.

You can try this same approach with MVC widgets if you also need a flexible layout (have different widgets display different sets of values from the current page).

Finally, all rules and patterns are meant to be broken when appropriate.

If you don't have complex business rules and instead are mapping page type fields directly to the view, then it might make sense to even put the DB access in the controller and have the page type be the view model.

This last approach trades loose coupling and the ability to decorate and apply cross cutting for the benefit of simplicity.

Kentico Experience 13 is going to have Route To View where you are provided a strongly typed view model that is your page type instance... no customer controllers or repositories are needed.

There are pros and cons to each approach. Some require more up front work but allow more flexibility while others are far faster (and maybe less complex).

The key is to make informed trade offs where you (and your fellow devs) understand and agree on the costs and benefits.

Content sites might not need an Onion architecture, just like CRUD apps might not need Event Sourcing and Kuberentes... 😁

Collapse
 
seangwright profile image
Sean G. Wright • Edited

I just realized I might have skipped answering your specific question 🤦‍♂️!

If you are using an Onion architecture, then you should have a domain class (HomePageData) that is in your Core project. By having it there you can't take a dependency on your data access implementation project (let's call it Data).

This means HomePageData will need to have a constructor that only accepts:

  • Primitive .NET type parameters
  • Other types in your Core project

So you can't use the generated HomePage page type class as a parameter.

Each layer should own its abstractions, which means the HomePageData and (if you are using a repository pattern) the IHomePageRepository should be defined in Core, while the Data project will have an implementation of IHomePageRepository which will return HomePageData.

Core

  • HomePageData (domain class)
  • IHomePageRepository (domain abstraction)

Data

  • HomePage (custom page type class)
  • KenticoHomePageRepository (implementation of domain abstraction)

Now your presentation project (let's call it Web) will rely on the abstraction and domain type and never access the types in Data directly:

public class HomePageController : Controller
{
    private readonly IHomePageRepository repo;

    public HomePageController(IHomePageRepository repo) => this.repo = repo;

    public ActionResult Index()
    {
        HomePageData data = repo.GetHomePage();

        return View(new HomePageViewModel(data));
    }
}

Notice here that it's ok for the HomePageViewModel to depend on the domain abstraction HomePageData - domain types are meant to be used everywhere since they don't have any volatile dependencies themselves, instead it's the implementations that we want to keep isolated.