Было предложено взглянуть на более современную реализацию SharpArchitecture, и я был направлен на проект MultiTenant .
Первое, что нужно отметить, это количество проектов. Насколько я знаю, на самом деле трудно сдерживать количество проектов, но есть несколько странных вариантов.
Я не совсем уверен, какой смысл разделять контроллеры на отдельную сборку или почему у нас есть отдельный проект для ApplicationServices.
Я не единственный, кто так думает, я думаю:
Тогда есть основной проект:
Лично я бы не стал создавать проект только для двух файлов, но я могу жить с этим. Мне не нравятся такие атрибуты, как DomainSignature. Мне трудно действительно сказать, что, кроме того, что я думаю, что они поощряют образ мышления, который помещает модель в Центр всего сущего. Я обычно гораздо больше интересуюсь тем, что что-то делает, чем как оно формируется.
Проект данных в основном связан с настройкой NHibernate через Fluent NHibernate.
Далее идет проект Framework. И там мы сталкиваемся со следующими интерфейсами маркеров. Мне действительно не нравятся маркерные интерфейсы, и наличие их здесь, кажется, не добавляет ничего важного для приложения.
Кажется, что многое происходит просто для того, чтобы попытаться получить запасной вариант для не арендатора, но проблема здесь в том, что обычно гораздо лучше быть откровенным о таких вещах. У вас есть CentralSession, и у вас есть TenantSession, и вы работаете с каждым по-своему. Это облегчает управление инфраструктурой и, как правило, приводит к более понятному коду.
Пока что все это было довольно неинтересно, я бы настоятельно рекомендовал объединить решение всего в два проекта: веб-проект и тестовый проект, но это все.
Теперь мы переходим к причудливым битам, проекту контроллеров. И там мы находим следующий фрагмент кода:
public class TenantListQuery : NHibernateQuery, ITenantListQuery
{
public IPagination<TenantViewModel> GetPagedList(int pageIndex, int pageSize)
{
var query = Session.QueryOver<Tenant>()
.OrderBy(customer => customer.Name).Asc;
var countQuery = query.ToRowCountQuery();
var totalCount = countQuery.FutureValue<int>();
var firstResult = (pageIndex - 1) * pageSize;
TenantViewModel viewModel = null;
var viewModels = query.SelectList(list => list
.Select(mission => mission.Id).WithAlias(() => viewModel.Id)
.Select(mission => mission.Name).WithAlias(() => viewModel.Name)
.Select(mission => mission.Domain).WithAlias(() => viewModel.Domain)
.Select(mission => mission.ConnectionString).WithAlias(() => viewModel.ConnectionString))
.TransformUsing(Transformers.AliasToBean(typeof(TenantViewModel)))
.Skip(firstResult)
.Take(pageSize)
.Future<TenantViewModel>();
return new CustomPagination<TenantViewModel>(viewModels, pageIndex, pageSize, totalCount.Value);
}
}
Мне очень нравится этот код. Явно о том, что он делает. Есть веская причина скрывать подобные вещи за классом, потому что, хотя он легко читается, он также содержит много подробного кода, который следует абстрагировать. Мне нравится использование фьючерсов, чтобы уменьшить количество запросов, и что у нас есть явная подкачка здесь. Мне также нравится проекция непосредственно в модель представления.
Что мне не нравится, так это то, что я действительно не понимаю, как выбирается экземпляр Session. О, я понимаю, как работает MultiTenantSessionFactoryKeyProvider, и что мы получаем центральную базу данных, потому что мы не используем арендованную сущность, но здесь все равно кажется слишком много волшебства, я бы предпочел вместо этого CentralSession.
Еще одна тонкость, которая мне понравилась, была структура кода:
Весь код очень красиво сгруппирован по функциям.
Мое главное недовольство приложением заключается в том, что это в основном все. Мы говорим о приложении, которое в основном состоит из двух страниц CRUD, не более того. Да, это пример приложения, чтобы показать что-то очень специфическое, но мне бы хотелось увидеть еще немного мяса, чтобы на него посмотреть.
Многопользовательская аренда — сложная проблема, и это приложение тратит довольно много времени на то, что по сути является управлением строкой соединения.