Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In your architecture, how do you decouple a url from the database layer & business objects layers

BACKGROUND

We've got some links on our site that have formats that look like:

http://oursite.com/books/c_sharp_in_depth_12345.

To handle this, we use a simple property called Url:

public class Book
{
    public string Url { get; set;}
}

Basically, the URL comes from our website's domain, a section of the site (i.e. books), the name of the page and a unique id to identify the resource. The full URL is being stored in the database.

We don't like the fact that our database is storing the section name. That's a property of web layer property, not a property of the book. The database should not be dependent upon the web layer.

So we remove the section from the URL and get the following:

public class Book
{
    public string UrlWithoutSection { get; set;}
}

OK, that works for this URL. But then our company's SEO czar says that our URL is wrong and that Google, our-one-true-love, will only love us if we re-write our urls like this:

http://oursite.com/programming-books/c-sharp-in-depth-12345

Uh, oh, I thought that we had removed the database dependency to the web layer, but we hadn't. Turns out, we had removed the dependency to the section, but the format of the URL still exists in the database. We fix this by abstracting the URL into an object:

public class OurUrl 
{
    public string title { get; set; }
    public string id { get; set; }
}

Cool, now the dependency to the web layer is gone. Uh oh, here this time our CEO comes to us. We just bought a new company and now we're selling magazines. Uh, great? The magazine URLs are going to look like this:

http://oursite.com/magazines/computers/stack-overflow-the-magazine/2012/01/01/12345

OK, no problem, just create another object.

public class OurMagazineUrl : OurUrl
{
    public DateTime PublishedDate { get; set; }

    // Magazine enum will have to be created.
    public MagazineType Type { get; set; }  
} 

It works, except then I begin to realize that we have plans for a big site. Lots of URLs. Lots of differently formatted URLs. Creating a new class every time seems like a major headache.

THE PROBLEM IN A NUTSHELL

How do you handle URLs so that the web layer is properly decoupled from the business layer and the data layer? I've come up with several thoughts about solutions:

MORE ABOUT THE PROBLEM

I'm hoping that this helps clarify some confusion.

We are using ASP.Net MVC. We use routes. We use helpers. We pass flattened DTOs to our web layer, not business objects. This problem concerns the service layer and an explosion of DTOs.

This is mainly a high traffic news site, not a line of business site. It can have many different urls and the urls can change at any time. They can be complex and arbitrarily determined by management.

URL Examples (not real, made up for example purposes).

 1. http://oursite.com/news/wi/politics/supreme-court/recent-ruling-someid
 2. http://oursite.com/news/wi/politics/election-2012/candidate-x-takes-stand-on-issue-y-someid
 3. http://oursite/com/news/politics/mayor-says-things-are-a-ok-someid
 4. http://oursite.com/news/milwaukee/local-bar-named-to-HOF-someid
 5. http://oursite.com/news/wi/politics/supreme-court-someid
 6. http://oursite.com/news/whatever-cat-our-CEO-wants/subcat1/subcat2/etc/2011/10/31/some-story-someid

All of the above are "articles" and we have an Article class. An article has a number of navigation properties, such as AuthorObject, RelatedLinksCollection, etc. Business objects are far too heavy to pass to a client, so we pass DTOs that flatten information (e.g. AuthorName). The above links, however can require different information, even though they are all "articles".

  1. needs Category, Subcategory, Title and Id
  2. needs Category,Subcategory, PoliticsCategory, Title and Id
  3. needs Category, Title and Id
  4. needs Category, Title and Id
  5. needs Category, Subcategory, Title and Id
  6. needs CeoCategory, CeoSubcategory, PublishedDate,Title and Id

In static programming languages, such as c#, the normal way would be to handle this would be to create separate DTO classes. You could add inheritance to reduce some of the code, but you still end up with multiple "article" dto classes.

public class IArticleDto { 
  public string title { get; set; } 
  public string body { get; set; } 
  public string Category { get; set; }}

public class StateArticleDto: IArticleDto { 
  public string title { get; set; } 
  public string body { get; set; } 
  public string Category { get; set; }}
  public string StateCode { get; set; } 
  public string Subcategory { get; set; } 
}

public class SupremeCourtArticleDto: IArticleDto { 
  public string title { get; set; } 
  public string body { get; set; } 
  public string Category { get; set; }}
  public string Subcategory { get; set; } 
}

public class ArbitraryCeoArticleDto: IArticleDto { 
//who knows
}

etc.

The ability to write custom urls in any way possible in not negotiable. If an article relates to something (state, Category, etc.), it can become part of the url.

Solutions?

  1. Continue to add Url objects as needed. How many? At least a dozen, but naming them will be troublesome. Doing one per business object solves the name issue, but that means dozens or hundreds of new objects. Yuck.

  2. IOC - Pass in the route pattern to the Data Access layer via configuration. The data access layer can then create a full url. The url pattern name is still a problem.

  3. Use a Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>, etc. to pull in.

  4. Use an Expando or DynamicObject for the url details. So url will contain a couple basic properties (name and id), but other properties could be added when necessary.

I'm thinking about using 4), because it seems like something that dynamic programming does better than static languages. However, it may just be that I'm looking at it most, because I like new toys to play with (I haven't used expando before).

It's bettern than 1), because of the object explosion. I'm not sure that 2) will work for complex scenarios. You could pass in simple route name + route information to the data layer using DI, but it seems harder to accomplish with no additional gain. And it probably wouldn't work for complex routes -- for that, I think that you need to have a rules engine on the UI side.

As compared to 3), I think that 4) is slightly better. Someone correct me if I'm mistaken, but dynamic types seem to be no more than syntatic sugar on top of a dictionary, but with the advantage of cleaner code. Just a thought.

like image 574
John Avatar asked Nov 04 '22 12:11

John


1 Answers

I'm going to be blunt and say that the fact that you're asking this question and based on the solutions you're proposing, you're not taking a birds-eye architectural view of the problem at hand. I don't know the intimate details of your application, but being able to have a bit outlook and the ability to picture what it might be used to in the future will help with the URI design.

The solutions you're proposing are all revolving around very concrete, programmatic details, while the problem is abstract and not related to programming at all. Solving the problem is all about writing down the entities in your domain, the actions that are possible to perform on them and how they relate to each other.

Bill de hÓra has a blog post regarding Web resource mapping criteria for frameworks (here's a cached version, should the real one still give HTTP 500 errors) and Joe Gregorio has written about how to RESTify DayTrader that should give you the right idea around how to envision the complete URI space of your application. Planning, drawing, thinking and writing is what it sounds like you and your team need to do.

When the complete scope and space of the URIs in your application is designed, you're ready to implement it. However you then do it is up to you, and then I'd recommend using URI Templates to define the URIs themselves and regular code to map the code that will handle the URIs (be it Controllers, Handlers or whatever). In ASP.NET MVC, the configuration code is written against the RouteTable class, while in OpenRasta it is done against a ResourceSpace class.

like image 168
Asbjørn Ulsberg Avatar answered Nov 14 '22 22:11

Asbjørn Ulsberg