我们可以在领域驱动设计中使用 ASP.NET 身份吗?

本文关键字:ASP NET 身份 我们 | 更新日期: 2023-09-27 17:56:05

我们的团队决定在我们的项目中使用领域驱动设计架构。现在的讨论正在进行中,"我们可以在DDD中使用 ASP.NET 标识吗?

在 DDD 设计中使用 ASP.NET 标识是否有任何缺点。

我很困惑,无法做出决定。

我已经搜索过它,但我不知道。

任何帮助都是可观的。

我们可以在领域驱动设计中使用 ASP.NET 身份吗?

这些问题揭示了几个误解:

您似乎将域模型视为某个整体模型,您将每个应用程序都放入其中。相反,请专注于战略模式以区分界定上下文。将域视为几个松散互连的组件的组合。然后确定您的核心域是什么,并在那里应用 DDD 战术模式。并非每个组件都需要 DDD。其中一些甚至不应该使用 DDD。特别是 - 通用域,如身份验证。

DDD 与技术无关(在某种程度上),所以是的,您可以使用 ASP.NET 标识或任何您喜欢的库。

身份验证通常属于应用程序层,而不是域层。

但是 - 如果您的域中有用户/客户端/人员的概念,则可能需要使用标识组件提供的标识。但你必须了解,用户在边界上下文中的含义与标识组件中的用户的含义不同。这些不是同一个概念。虽然它们都是指坐在某个地方并单击应用程序的 GUI 的同一个物理人,但它们是他的 2 个不同模型(或投影),用于不同的目的。因此,不应只在界定上下文中重用 ASP.NET User 类。

相反,单独的上下文应通过防腐层进行通信。基本上,您必须在边界上下文中创建一些服务(仅限接口),以生成特定于上下文的用户对象。在基础结构层中创建的该接口的实现将是 ASP.NET 标识的包装器,该包装器获取 ASP.NET 标识用户并生成相应的边界上下文的用户。

我是DDD的新手。但我实现了与 Identity 和 DDD 的集成。虽然我怀疑它是否真的坚持 DDD 的原则。

我从实体开始:

public partial class User : IdentityUser {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager) {
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }
        /// <summary>
        /// The Date the User Registered for general information purposes
        /// </summary>
        public DateTime DateRegistered { get; set; }
    }

然后界面:

public interface IUserRepository:IBaseRepository<User> {
        /// <summary>
        /// Register User to Identity Database
        /// </summary>
        /// <param name="userManager">User Manager to Handle Registration</param>
        /// <param name="user">User to add to database</param>
        /// <param name="password">User's password</param>
        /// <returns>Identity Result</returns>
        Task<IdentityResult> Register(UserManager<User, string> userManager, User user, string password);
        /// <summary>
        /// Login User
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle login</param>
        /// <param name="email">Email of user</param>
        /// <param name="password">Password of user</param>
        /// <param name="rememberMe">Boolean if the user wants to be remembered</param>
        /// <returns>SignIn Status</returns>
        Task<SignInStatus> Login(SignInManager<User, string> signinManager, string email, string password, bool rememberMe);
        /// <summary>
        /// Verify that code sent to User is valid
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle verification</param>
        /// <param name="provider">Provider of the code</param>
        /// <param name="code">The code</param>
        /// <param name="rememberMe">Boolean if user wants to be remembered</param>
        /// <param name="rememberBrowser">Boolean if browser should be remembered</param>
        /// <returns>SignIn Status</returns>
        Task<SignInStatus> VerifyCode(SignInManager<User, string> signinManager, string provider, string code, bool rememberMe, bool rememberBrowser);
        /// <summary>
        /// Confirm email of User
        /// </summary>
        /// <param name="userManager">User Manager to handle confirmation</param>
        /// <param name="userId">String user Id of the User</param>
        /// <param name="code">User code sent in Email</param>
        /// <returns>Identity Result</returns>
        Task<IdentityResult> ConfirmEmail(UserManager<User, string> userManager, string userId, string code);
        void ForgotPassword();
        void ForgotPasswordConfirmation();
        void ResetPassword();
        void ResetPasswordConfirmation();
        void ExternalLogin();
        void SendCode();
        void ExternalLoginCallback();
        void ExternalLoginConfirmation();
        /// <summary>
        /// Log off user from the Application
        /// </summary>
        /// <param name="AuthenticationManager">Application Manager to handle Sign out</param>
        void Logoff(IAuthenticationManager AuthenticationManager);
        /// <summary>
        /// Get user based on their Email
        /// </summary>
        /// <param name="Email">Email of user</param>
        /// <returns>User</returns>
        User GetUser(string Email);
        /// <summary>
        /// Get User by their GUID
        /// </summary>
        /// <param name="ID">GUID</param>
        /// <returns>User</returns>
        User GetUserById(string ID);
    }

然后存储库:

public class UserRepository : BaseRepository<User>, IUserRepository {
        /// <summary>
        /// Confirm email of User
        /// </summary>
        /// <param name="userManager">User Manager to handle confirmation</param>
        /// <param name="userId">String user Id of the User</param>
        /// <param name="code">User code sent in Email</param>
        /// <returns>Identity Result</returns>
        public async Task<IdentityResult> ConfirmEmail(UserManager<User, string> userManager, string userId, string code) =>
            await userManager.ConfirmEmailAsync(userId, code);
        public void ExternalLogin() {
            throw new NotImplementedException();
        }
        public void ExternalLoginCallback() {
            throw new NotImplementedException();
        }
        public void ExternalLoginConfirmation() {
            throw new NotImplementedException();
        }
        public void ForgotPassword() {
            throw new NotImplementedException();
        }
        public void ForgotPasswordConfirmation() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Get user based on their Email
        /// </summary>
        /// <param name="Email">Email of user</param>
        /// <returns>User</returns>
        public User GetUser(string Email) =>
            _context.Users.Where(p => p.Email == Email).FirstOrDefault();
        /// <summary>
        /// Get User by their GUID
        /// </summary>
        /// <param name="ID">GUID</param>
        /// <returns>User</returns>
        public User GetUserById(string ID) => 
            _context.Users.Find(ID);
        /// <summary>
        /// Login User
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle login</param>
        /// <param name="email">Email of user</param>
        /// <param name="password">Password of user</param>
        /// <param name="rememberMe">Boolean if the user wants to be remembered</param>
        /// <returns>SignIn Status</returns>
        public async Task<SignInStatus> Login(SignInManager<User, string> signinManager, string email, string password, bool rememberMe) =>
            await signinManager.PasswordSignInAsync(email, password, rememberMe, shouldLockout: false);
        /// <summary>
        /// Log off user from the Application
        /// </summary>
        /// <param name="AuthenticationManager">Application Manager to handle Sign out</param>
        public void Logoff(IAuthenticationManager AuthenticationManager) {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
        }
        /// <summary>
        /// Register User to Identity Database
        /// </summary>
        /// <param name="userManager">User Manager to Handle Registration</param>
        /// <param name="user">User to add to database</param>
        /// <param name="password">User's password</param>
        /// <returns>Identity Result</returns>
        public async Task<IdentityResult> Register(UserManager<User, string> userManager, User user, string password) =>
            await userManager.CreateAsync(user, password);
        public void ResetPassword() {
            throw new NotImplementedException();
        }
        public void ResetPasswordConfirmation() {
            throw new NotImplementedException();
        }
        public void SendCode() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Verify that code sent to User is valid
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle verification</param>
        /// <param name="provider">Provider of the code</param>
        /// <param name="code">The code</param>
        /// <param name="rememberMe">Boolean if user wants to be remembered</param>
        /// <param name="rememberBrowser">Boolean if browser should be remembered</param>
        /// <returns>SignIn Status</returns>
        public async Task<SignInStatus> VerifyCode(SignInManager<User, string> signinManager, string provider, string code, bool rememberMe, bool rememberBrowser) =>
            await signinManager.TwoFactorSignInAsync(provider, code, isPersistent: rememberMe, rememberBrowser: rememberBrowser);
    }

服务:

public interface IUserService {
        /// <summary>
        /// Register User to Identity Database
        /// </summary>
        /// <param name="userManager">User Manager to Handle Registration</param>
        /// <param name="user">User to add to database</param>
        /// <param name="password">User's password</param>
        /// <returns></returns>
        Task<IdentityResult> Register(UserManager<User, string> userManager, User user, string password);
        /// <summary>
        /// Login User
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle login</param>
        /// <param name="email">Email of user</param>
        /// <param name="password">Password of user</param>
        /// <param name="rememberMe">Boolean if the user wants to be remembered</param>
        /// <returns></returns>
        Task<SignInStatus> Login(SignInManager<User, string> signinManager, string email, string password, bool rememberMe);
        /// <summary>
        /// Verify that code sent to User is valid
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle verification</param>
        /// <param name="provider">Provider of the code</param>
        /// <param name="code">The code</param>
        /// <param name="rememberMe">Boolean if user wants to be remembered</param>
        /// <param name="rememberBrowser">Boolean if browser should be remembered</param>
        /// <returns></returns>
        Task<SignInStatus> VerifyCode(SignInManager<User, string> signinManager, string provider, string code, bool rememberMe, bool rememberBrowser);
        /// <summary>
        /// Confirm email of User
        /// </summary>
        /// <param name="userManager">User Manager to handle confirmation</param>
        /// <param name="userId">String user Id of the User</param>
        /// <param name="code">User code sent in Email</param>
        /// <returns></returns>
        Task<IdentityResult> ConfirmEmail(UserManager<User, string> userManager, string userId, string code);
        void ForgotPassword();
        void ForgotPasswordConfirmation();
        void ResetPassword();
        void ResetPasswordConfirmation();
        void ExternalLogin();
        void SendCode();
        void ExternalLoginCallback();
        void ExternalLoginConfirmation();
        /// <summary>
        /// Log off user from the Application
        /// </summary>
        /// <param name="AuthenticationManager">Application Manager to handle Sign out</param>
        void Logoff(IAuthenticationManager AuthenticationManager);
        /// <summary>
        /// Get user based on their Email
        /// </summary>
        /// <param name="Email">Email of user</param>
        /// <returns>User</returns>
        User GetUser(string Email);
        /// <summary>
        /// Get User by their GUID
        /// </summary>
        /// <param name="ID">GUID</param>
        /// <returns>User</returns>
        User GetUserById(string ID);
    }

服务:

public class UserService : ServiceBase, IUserService {
        #region Private Field
        private IUserRepository _userRepository;
        #endregion
        #region Constructor
        /// <summary>
        /// Constructor to initialise User Repository
        /// </summary>
        /// <param name="userRepository"></param>
        public UserService(IUserRepository userRepository) {
            _userRepository = userRepository;
        }
        #endregion
        #region Methods
        /// <summary>
        /// Confirm email of User
        /// </summary>
        /// <param name="userManager">User Manager to handle confirmation</param>
        /// <param name="userId">String user Id of the User</param>
        /// <param name="code">User code sent in Email</param>
        /// <returns>Identity Result</returns>
        public Task<IdentityResult> ConfirmEmail(UserManager<User, string> userManager, string userId, string code) =>
            _userRepository.ConfirmEmail(userManager, userId, code);
        public void ExternalLogin() {
            throw new NotImplementedException();
        }
        public void ExternalLoginCallback() {
            throw new NotImplementedException();
        }
        public void ExternalLoginConfirmation() {
            throw new NotImplementedException();
        }
        public void ForgotPassword() {
            throw new NotImplementedException();
        }
        public void ForgotPasswordConfirmation() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Get user based on their Email
        /// </summary>
        /// <param name="Email">Email of user</param>
        /// <returns>User</returns>
        public User GetUser(string Email) {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Get User by their GUID
        /// </summary>
        /// <param name="ID">GUID</param>
        /// <returns>User</returns>
        public User GetUserById(string ID) {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Login User
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle login</param>
        /// <param name="email">Email of user</param>
        /// <param name="password">Password of user</param>
        /// <param name="rememberMe">Boolean if the user wants to be remembered</param>
        /// <returns>SignIn Status</returns>
        public Task<SignInStatus> Login(SignInManager<User, string> signinManager, string email, string password, bool rememberMe) =>
            _userRepository.Login(signinManager, email, password, rememberMe);
        /// <summary>
        /// Log off user from the Application
        /// </summary>
        /// <param name="AuthenticationManager">Application Manager to handle Sign out</param>
        public void Logoff(IAuthenticationManager AuthenticationManager) {
            _userRepository.Logoff(AuthenticationManager);
        }
        /// <summary>
        /// Register User to Identity Database
        /// </summary>
        /// <param name="userManager">User Manager to Handle Registration</param>
        /// <param name="user">User to add to database</param>
        /// <param name="password">User's password</param>
        public Task<IdentityResult> Register(UserManager<User, string> userManager, User user, string password) =>
            _userRepository.Register(userManager, user, password);
        public void ResetPassword() {
            throw new NotImplementedException();
        }
        public void ResetPasswordConfirmation() {
            throw new NotImplementedException();
        }
        public void SendCode() {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Verify that code sent to User is valid
        /// </summary>
        /// <param name="signinManager">Signin Manager to handle verification</param>
        /// <param name="provider">Provider of the code</param>
        /// <param name="code">The code</param>
        /// <param name="rememberMe">Boolean if user wants to be remembered</param>
        /// <param name="rememberBrowser">Boolean if browser should be remembered</param>
        /// <returns>SignIn Status</returns>
        public Task<SignInStatus> VerifyCode(SignInManager<User, string> signinManager, string provider, string code, bool rememberMe, bool rememberBrowser) =>
            _userRepository.VerifyCode(signinManager, provider, code, rememberMe, rememberBrowser);
        #endregion
    }

你可以使用任何你喜欢的东西。但要注意污染,特定的解决方案将要做出。如果您的域模型被数百行 asp.net 技术管道代码弄乱,使您的域逻辑难以感知,并且您错过了 DDD 点。

在理想情况下 - 您的领域模型应仅依赖于编程语言。

另外 - 您可能会从我很久以前实现的用户会话相关代码中找到一些有用的东西。