использование Синглтона для передачи учетных данных в мультитенантном приложении запах кода?
В настоящее время я работаю над мультитенантным приложением, которое использует подход Shared DB/Shared Schema. IOW, мы применяем сегрегацию данных арендатора, определяя столбец TenantID во всех таблицах. По соглашению, все операции чтения/записи SQL должны включать в себя , где TenantID = '?" пункт. Не идеальное решение, но ретроспектива-20/20.
В любом случае, поскольку практически каждая страница / рабочий процесс в нашем приложении должны отображать данные конкретного клиента, я принял (плохое) решение в начале проекта, чтобы использовать Синглтон для инкапсуляции учетные данные текущего пользователя (т. е. идентификатора и идентификатор пользователя). В то время я думал, что не хочу добавлять параметр TenantID к каждой сигнатуре метода в моем слое данных.
Вот как выглядит основной псевдокод:
public class UserIdentity
{
public UserIdentity(int tenantID, int userID)
{
TenantID = tenantID;
UserID = userID;
}
public int TenantID { get; private set; }
public int UserID { get; private set; }
}
public class AuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthenticateRequest +=
new EventHandler(context_AuthenticateRequest);
}
private void context_AuthenticateRequest(object sender, EventArgs e)
{
var userIdentity = _authenticationService.AuthenticateUser(sender);
if (userIdentity == null)
{
//authentication failed, so redirect to login page, etc
}
else
{
//put the userIdentity into the HttpContext object so that
//its only valid for the lifetime of a single request
HttpContext.Current.Items["UserIdentity"] = userIdentity;
}
}
}
public static class CurrentUser
{
public static UserIdentity Instance
{
get { return HttpContext.Current.Items["UserIdentity"]; }
}
}
public class WidgetRepository: IWidgetRepository{
public IEnumerable<Widget> ListWidgets(){
var tenantId = CurrentUser.Instance.TenantID;
//call sproc with tenantId parameter
}
}
Как вы можете видеть, здесь есть несколько запахов кода. Это синглтон, так что это уже не юнит-тест дружественный. Кроме того, у вас есть очень тесная связь между CurrentUser и HttpContext объект. В расширенном смысле это также означает, что у меня есть ссылка на систему.Паутина в моем слое данных (содрогается).
Я хочу погасить некоторые технические долги в этом спринте, избавившись от этого синглтона по причинам, упомянутым выше. У меня есть несколько мыслей о том, что может быть лучше, но если у кого-то есть какие-либо рекомендации или извлеченные уроки, которыми они могли бы поделиться, я был бы очень признателен.1 ответ:
CurrentUser-это не совсем синглтон. Я не совсем уверен , Как бы вы это назвали. (Синглет по определению может существовать только один за раз, и любое количество экземпляров UserIdentity может быть создано по желанию внешним кодом и сосуществовать без каких-либо проблем.)
Лично я бы взял CurrentUser.Экземпляр и либо переместить его в UserIdentity.CurrentUser, или соедините его с любыми аналогичными методами и свойствами" get the global instance", которые у вас есть. Избавляется от текущего пользователя класс, по крайней мере. В то время как вы это делаете, сделайте свойство настраиваемым в том же месте-оно уже настраивается, просто таким образом, что (1) выглядело бы как волшебство, если бы два класса не были показаны рядом друг с другом, и (2) делает изменение способа установки текущего идентификатора пользователя позже сложнее.
Не избавляет от глобального, но вы действительно не сможете обойти это, не передав UserIdentity каждой функции, которая нуждается в нем.