В вашей архитектуре, как вы отделите URL от слоя базы данных и слоев бизнес-объектов - PullRequest
5 голосов
/ 29 октября 2011

ФОН

У нас есть несколько ссылок на нашем сайте, которые имеют следующие форматы:

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

Чтобы справиться с этим, мы используем простое свойство с именем Url:

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

В основном, URL-адрес берется из домена нашего веб-сайта, раздела сайта (например, книги), названия страницы и уникального идентификатора, идентифицирующего ресурс. Полный URL хранится в базе данных.

Нам не нравится тот факт, что в нашей базе данных хранится имя раздела. Это свойство свойства веб-слоя, а не свойство книги. База данных не должна зависеть от веб-слоя.

Таким образом, мы удаляем раздел из URL и получаем следующее:

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

ОК, это работает для этого URL. Но затем SEO-царь нашей компании говорит, что наш URL неверен и что Google, наша единственная настоящая любовь, будет любить нас, только если мы переписываем наши URL-адреса следующим образом:

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

О-о, я думал, что мы удалили зависимость базы данных от веб-слоя, но мы этого не сделали. Оказывается, мы удалили зависимость от раздела, но формат URL все еще существует в базе данных. Мы исправляем это, абстрагируя URL в объект:

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

Круто, теперь зависимость от веб-слоя исчезла. О, вот на этот раз наш генеральный директор приходит к нам. Мы только что купили новую компанию, и теперь мы продаем журналы. Отлично? URL-адреса журнала будут выглядеть так:

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

ОК, нет проблем, просто создайте другой объект.

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

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

Это работает, за исключением того, что я начинаю понимать, что у нас есть планы на большой сайт. Много URL-адресов. Много разных форматов URL. Создание нового класса каждый раз кажется большой головной болью.

проблема в ореховой скорлупе

Как вы обрабатываете URL-адреса, чтобы веб-уровень был правильно отделен от бизнес-уровня и уровня данных? У меня есть несколько мыслей о решениях:

БОЛЬШЕ О ПРОБЛЕМЕ

Я надеюсь, что это поможет прояснить некоторую путаницу.

Мы используем ASP.Net MVC. Мы используем маршруты. Мы используем помощников. Мы передаем плоские DTO нашему веб-слою, а не бизнес-объектам. Эта проблема касается уровня обслуживания и взрыва DTO.

В основном это новостной сайт с большим трафиком, а не бизнес-сайт. Он может иметь много разных URL-адресов, и URL-адреса могут меняться в любое время. Они могут быть сложными и произвольно определяться руководством.

Примеры URL (ненастоящие, составлены для примера).

 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

Все вышеперечисленное является «статьями», и у нас есть класс Article. Статья имеет ряд навигационных свойств, таких как AuthorObject, RelatedLinksCollection и т. Д. Бизнес-объекты слишком тяжелы для передачи клиенту, поэтому мы передаем DTO, которые выравнивают информацию (например, AuthorName). Приведенные выше ссылки, однако, могут требовать разной информации, даже если они все являются «статьями».

  1. нужна категория, подкатегория, заголовок и идентификатор
  2. нужна категория, подкатегория, политикаКатегория, название и идентификатор
  3. нужна категория, название и идентификатор
  4. нужна категория, название и идентификатор
  5. нужна категория, подкатегория, заголовок и идентификатор
  6. требуется CeoCategory, CeoSubcategory, Опубликованная дата, название и идентификатор

В статических языках программирования, таких как c #, обычным способом было бы обработать это, создав отдельные классы DTO. Вы можете добавить наследование, чтобы уменьшить часть кода, но у вас все равно останется несколько классов "article" dto.

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
}

и т.д.

Возможность писать собственные URL-адреса любым возможным способом, не подлежащим обсуждению. Если статья относится к чему-либо (состояние, категория и т. Д.), Она может стать частью URL.

Решения

  1. Продолжайте добавлять Url объектов по мере необходимости.Как много?По крайней мере, дюжина, но назвать их будет проблематично.Выполнение одного для бизнес-объекта решает проблему с именем, но это означает, что десятки или сотни новых объектов.Гадость.

  2. IOC - передать шаблон маршрута на уровень доступа к данным через конфигурацию.Слой доступа к данным может затем создать полный URL-адрес.Имя шаблона URL все еще является проблемой.

  3. Используйте Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue> и т. Д. Для ввода.

  4. Используйте Expando или DynamicObject длядетали URL.Таким образом, url будет содержать несколько основных свойств (name и id), но при необходимости могут быть добавлены другие свойства.

Я думаю об использовании 4), потому что этопохоже, что динамическое программирование работает лучше, чем статические языки.Однако, возможно, я просто смотрю на это больше всего, потому что мне нравятся новые игрушки, с которыми я могу играть (раньше я не использовал expando).

Это лучше, чем 1) из-за взрыва объекта,Я не уверен, что 2) будет работать для сложных сценариев.Вы можете передать простое имя маршрута + информацию о маршруте на уровень данных, используя DI, но это кажется труднее сделать без дополнительного усиления.И это, вероятно, не будет работать для сложных маршрутов - для этого, я думаю, вам нужно иметь механизм правил на стороне пользовательского интерфейса.

По сравнению с 3), я думаю, что 4) немного лучше,Кто-то исправит меня, если я ошибаюсь, но динамические типы кажутся не более чем синтетическим сахаром поверх словаря, но с преимуществом более чистого кода.Просто мысль.

Ответы [ 4 ]

2 голосов
/ 31 октября 2011

Я буду откровенен и скажу, что тот факт, что вы задаете этот вопрос и основываясь на решениях, которые вы предлагаете, вы не рассматриваете проблему с высоты птичьего полета.Я не знаю подробных подробностей вашего приложения, но возможность иметь немного перспективы и возможность представить, к чему это может привести в будущем, поможет с дизайном URI.

Решения, которые выВы предлагаете вращаться вокруг очень конкретных, программных деталей, в то время как проблема абстрактна и вообще не связана с программированием.Решение проблемы сводится к тому, чтобы записать сущности в вашем домене, действия, которые можно выполнять над ними, и как они связаны друг с другом.

У Билла де Хура есть запись в блоге относительно веб-ресурсаКритерии отображения для фреймворков ( - это кэшированная версия , если реальная версия все еще дает ошибки HTTP 500), и Джо Грегорио написал о том, как RESTify DayTrader , что должно датьправильная идея о том, как представить полное пространство URI вашего приложения.Планирование, рисование, мышление и письмо - это то, что вам и вашей команде нужно делать.

Когда весь объем и пространство URI в вашем приложении спроектированы, вы готовы его реализовать.Тем не менее, вы делаете это сами, и тогда я бы рекомендовал использовать URI Templates для определения самих URI и обычного кода для сопоставления кода, который будет обрабатывать URI (будь то Controller s,Handler с или что угодно).В ASP.NET MVC код конфигурации написан для класса RouteTable , а в OpenRasta - для класса ResourceSpace .

0 голосов
/ 31 октября 2011

Вот мой первый заход на # 4. Это работает, и динамический тип объекта работает элегантно.

public class ArticleDto 
{
//normal Article properties
//code goes here

//new dyamic property for the Uri Details
public dynamic UriDetails { get; set; }
}

Затем я создал собственный класс для обработки деталей URI. Я добавил конструктор с именем + id, потому что я всегда хочу, чтобы имя ресурса + id передавалось без исключений.

public class UriDetails : DynamicObject
{
    //TODO put error handling in here to ensure that they're not empty?
    public string ResourceName { get; set; }
    public int ResourceId { get; set; }

    public UriDetails(int resourceId, string resourceName)
    {
        ResourceId = resourceId;
        ResourceName = resourceName;
    }
//other code that you need to override
}

Тогда в моем коде DataAccess

ArticleDto = new ArticleDto();
//Set the regular properties
//code goes here

//Set the mandatory uri properties
ArticleDto.UriDetails = new UriDetails(id, articleTitle);

//Set any other properties specific to this call
ArticleDto.Date = publishedDate;

Одно примечание: это решает проблему уровня сервиса, но веб-слою все еще нужно знать, как создать URL. Это будет некоторый тип вспомогательного класса / механизма правил, который будет определять, что используется, на основе имен и типов свойств в UriDetails.

Дайте мне знать, что вы думаете.

0 голосов
/ 31 октября 2011

может быть конечный автомат, который вы могли бы поддерживать внешний DSL.

Main
.*=Product
nj|ny=State
([a-z]*-)*=Title
20[1-9][1-9]->ExpectMonth =Year 
ExpectMonth
[0|1][0-9] = Month -> ExpectDay

ect ....

И T4 для генерации кода структуры

struct Url {
 string Product {get;set}
 string State {get;set;}
}

Я думаю, что антипаттерн "монолитный класс" несколько смягчается генерацией кода.

0 голосов
/ 31 октября 2011

Вы правы, когда видите необходимость отделить сущности вашего домена (книги, журналы и т. Д.) От любой информации об их URL.

Единственный идентификатор, который вам необходим для доступа к книге (например).Id - в вашем примере 12345 .Любые другие элементы таксономии должны обрабатываться в логике представления, как, возможно, вам нужна другая структура URI для каналов не из WWW?А если запустить сайт многоязычный?Наличие / журналов / компьютеров / в базе данных будет препятствием.

Требования вашего царя SEO могут со временем меняться, поскольку меняются и методы повышения ранга в поисковых системах.

Таким образом, речь идет о маршрутизации URL .

В решении ASP.NET Webforms вы добавили бы следующие записи в свой файл Global.asax:

void Application_Start(object sender, EventArgs e) 
{
    RegisterRoutes(System.Web.Routing.RouteTable.Routes);
}

public static void RegisterRoutes(System.Web.Routing.RouteCollection routes)
{
    routes.MapPageRoute("Book",
        "{productType}/{categoryName}/{productName}/{productId}",
        "~/Books.aspx");

    routes.MapPageRoute("Magazine",
        "{productType}/{categoryName}/{productName}/{year}/{month}/{day}/{productId}",
        "~/Magazines.aspx");
}

Books.aspx и Magazines.aspx будут собирать соответствующие частиURL с:

var categoryName = Page.RouteData.Values["categoryName"];

, и когда вы собрали достаточно информации для уникальной идентификации продукта, который хотите отобразить, вы можете запросить свой домен / уровень данных, чтобы получить необходимую информацию.

Когда становятся доступны новые типы продуктов (или ваши заинтересованные стороны запрашивают новую структуру URL), вам просто нужно добавить другой маршрут.

...