Register | Login

Stacking Code

public interface IBlog { string Dump(Stream consciousness); }

View Model Refactor

Monday, 13 December, 2010 @ 7:40 PM < Adam Boddington
Tags: Architecture, Building Neno, Linq, NHibernate, T-SQL

This is post #20 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.

I want to add comments to my posts. The easiest way I can think of to do that, in a disabled JavaScript friendly way, is to have a form on the individual post screen. That screen currently accepts a post domain object as its post model. While great for showing the details of a post, it's not going to easily handle my comment postback without some ugly workarounds. (I know, I spent a couple of hours trying.)

I need to acknowledge the fact my domain objects are not going to make very good view models going forward. I need to create space in my web application for explicit view models, and that includes refactoring the confused, de facto, implicit view models I already have.

What's In a Name?

If you like elegant code, then everything.

The first thing I'm going to do is rename my Models namespace to ViewModels. Models is too ambiguous for me -- I already have a domain model in my application assembly -- I want to be explicit about what kind of models these will be.

Why ViewModels and not ViewModel, like the DomainModel namespace? A domain model is just one model, made up of many classes. View models on the other hand, are (usually) independent models for single views. It's personal preference in the end, but I prefer to acknowledge the collection in the namespace.

Next, I'm going to move the PublishDate family of classes to a new namespace, RouteModels. That's really what those are, some helper classes for route data. They're not view models.

Some other classes don't belong in the ViewModels namespace either. the IIdentityExtensions class can go in the application assembly since it has no web dependency and could conceivably be reused in another authentication environment (like Windows authentication).

The AuthCookie class is also not a view model. I'll move that up into the parent WebApplication namespace. It could be reused if I build another forms authentication UI, but that's unlikely at the moment, so it can stay in this UI for now.

That's Classy, That Is

My terrible cockney accent doesn't convey, but the general gist is I'm going to rename my view model classes too. I'm using a bunch of de facto view models right now with names like...

  • Archive
  • ArchiveYear
  • ArchiveMonth
  • OlderNewerArchive
  • OlderNewer
  • OlderNewerPost
  • OlderNewerPosts

Not very well named view models. What do they mean is probably the biggest question. Who is using them is probably the next.

I'm going to refactor them into view models named for the views that use them. Some view models will be reused, so I'll either choose a name that makes sense to both, or name it after the first view to use it. Naming view models after views is going to lead to some duplication, so I'll use extra namespaces based on controller names (the same way views are broken up into folders).

The gizzards are being refactored too. The smelly Payload property is gone, replaced by better named properties. The nonsensical inheritance chain of OlderNewer is also gone.

Group By

While I'm refactoring, I'm going to address the code smell in one of the view models. The Archive view model, now the Posts and PostsByPublishYear view models, was accepting large lists of publish dates and grouping them in memory using Linq. I'm going to make the database do the grouping work for me and cut down on the amount of data being passed around.

I don't normally refactor for performance when there's no performance issue, but this code smell is offending my RDBMS sensibilities.

I'm having trouble getting grouping to work in NHibernate Linq, so in the interest of just getting this done I'm going to fall back to ICriteria, specifically the new wrapper for it, QueryOver.

public IEnumerable<object[]> GetYearlyCounts()
{
    return Session.QueryOver<Post>()
        .Select(
            Projections.SqlGroupProjection("DATEPART(year, {alias}.[PublishDate]) AS [PublishYear]", "DATEPART(year, {alias}.[PublishDate])", new[] { "PublishYear" }, new[] { NHibernateUtil.Int32 }),
            Projections.SqlGroupProjection("DATEPART(month, {alias}.[PublishDate]) AS [PublishMonth]", "DATEPART(month, {alias}.[PublishDate])", new[] { "PublishMonth" }, new[] { NHibernateUtil.Int32 }),
            Projections.Count<Post>(post => post.PublishDate))
        .List<object[]>();
}

While a reasonably light query, I don't like the T-SQL dependency. And it gives me a small dataset of years, months and post counts that is still not easy to consume. I may look at this again, but in the meantime I'll plough ahead and address the consumability issue at least.

public IEnumerable<YearlyPostCount> GetYearlyCounts()
{
    return Session.QueryOver<Post>()
        .Select(
            Projections.SqlGroupProjection("DATEPART(year, {alias}.[PublishDate]) AS [PublishYear]", "DATEPART(year, {alias}.[PublishDate])", new[] { "PublishYear" }, new[] { NHibernateUtil.Int32 }),
            Projections.SqlGroupProjection("DATEPART(month, {alias}.[PublishDate]) AS [PublishMonth]", "DATEPART(month, {alias}.[PublishDate])", new[] { "PublishMonth" }, new[] { NHibernateUtil.Int32 }),
            Projections.Count<Post>(post => post.PublishDate))
        .List<object[]>()
        // End of NHibernate call, now transform in memory.
        .GroupBy(row => (int)row[0], row => new[] { (int)row[1], (int)row[2] })
        .Select(year => new YearlyPostCount(year.Key, year
            .GroupBy(row => row[0], row => row[1])
            .Select(month => new MonthlyPostCount(year.Key, month.Key, month.First()))));
}

This gives me some nested lists I can just iterate through in my UI. That will do for now.

Conclusion

After all that my application looks exactly the same as it did yesterday, but at least I'm happier about the direction my code is going. Staying clear, flexible, and easy to maintain can be just as important as adding features.

There are 0 comments.


Comments

Leave a Comment

Please register or login to leave a comment.


Older
Post Screens

Newer
Comments

Older
Post Screens

Newer
Comments

browse with Pivot


About


Projects

Building Neno


RSS
Recent Posts

Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue


Tags

Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)


Archives


Powered by Neno, ASP.NET MVC, NHibernate, and small furry mammals. Copyright 2010 - 2011 Adam Boddington.
Version 1.0 Alpha (d9e7e4b68c07), Build Date Sunday, 30 January, 2011 @ 11:37 AM