码迷,mamicode.com
首页 > 其他好文 > 详细

探讨DDD中角色权限与DCI的使用

时间:2016-08-19 00:36:40      阅读:548      评论:0      收藏:0      [点我收藏+]

标签:

本文初衷

之前在学习DDD的时候,一直被权限与角色困扰。

我们知道在Asp.net MVC 的Controller或Action加上特性标签[Authorize],就可以实现权限控制。

  [Authorize(Roles ="Manager")]
    public class MainController : Controller
    {
        // GET: Main
        public ActionResult Index()
        {
            return View();
        }
    }

但是在应用层中,如何使用好角色?在领域层如何设计好角色?如何使角色系统变得更优雅,更易扩展?

上述问题将是本位探讨的内容。

问题引入

我们模拟开发一个简单的公司内部审批系统,有以下两种角色:

1.经理:审批申请单。 

2.员工:提出请假申请单。

设计如下:

角色接口:

    public interface IRole
    {

        
    }

 

a.经理接口:

    public interface IManager : IRole
    {
        /// <summary>
        /// 批准申请单
        /// </summary>
        /// <param name="applyOrder"></param>
        void ApprovalOrder(ApplyOrder applyOrder);

    }

 

b.员工接口:

   /// <summary>
    /// 员工接口
    /// </summary>
    public interface IEmployee : IRole
    {
        /// <summary>
        /// 提交申请
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        ApplyOrder ApplyNewOrder(string content);

    }

 

c.用户类:

 public class User
    {

        public User()
        {
            Roles = new List<string>();
        }

        public string Id { get; set; }

        /// <summary>
        /// 名字
        /// </summary>
        public string Name { get; set; }


        /// <summary>
        /// 用户的角色字符串
        /// </summary>
        public List<string> Roles { get; set; }

    }

有了以上的基础设计,我们可以继续往下开发系统的角色扮演这块的代码了。

我们先引入两个不优雅的设计,来讨论一下他们的缺陷。

不优雅的设计1:在User对象里面增加角色扮演方法

 我们在User里面增加角色扮演方法:

public class User
    {
    //其他信息参考上面
        public TRole ActAs<TRole>() where TRole : class,IRole
        {
            if(typeof(TRole) is IManager)
            {
          //此处为了简化演示,直接在这里判定
if (Roles.Contains("Manager")) { var role = new Manager(this); return role as TRole; } throw new Exception("你不是经理"); } else if(typeof(TRole) is IEmployee) {
          //此处为了简化演示,直接在这里判断
if (Roles.Contains("Employee")) { var role = new Employee(this); return role as TRole; } throw new Exception("你不是员工"); } throw new NotImplementedException("还未实现对应的角色转换"); } }

我们很容易看到上述代码的缺陷:对扩展不友好,一旦要增加其他角色,就要修改User类的代码。

不优雅的设计2:在User对象里面增加角色扮演方法

我们考虑让User类实现IManager接口和IEmployee接口

public class User : IEmployee,IManager
    {
     //其他信息参考上面
    
public TRole ActAs<TRole>() where TRole : class,IRole { if(typeof(TRole) is IManager) { if (Roles.Contains("Manager")) {
            return this as TRole; } throw new Exception("你不是经理"); } else if(typeof(TRole) is IEmployee) { if (Roles.Contains("Employee")) { return this as TRole; } throw new Exception("你不是员工"); } throw new NotImplementedException("还未实现对应的角色转换"); } public ApplyOrder ApplyNewOrder(string content) { throw new NotImplementedException(); } public void ApprovalOrder(ApplyOrder applyOrder) { throw new NotImplementedException(); } }

我们也来分析一下上面代码不好的地方

1.如果系统有很多的角色,会造成User类要实现的角色非常多,不符合单一职责原则。

2.和设计1类似,对扩展不友好,一旦要增加其他角色,就要修改User类的代码。

好的设计

首先我们先修改IRole接口:

    public interface IRole
    {
        User User { get; }
    }

 引入角色扮演工厂接口:

    public interface IRoleAct<T> where T :IRole
    {
         T ActAs(User user);
    }

定义经理的扮演工厂接口:

    public interface IManagerRoleAct : IRoleAct<IManager>
    {         

    }

定义经理的扮演工厂实现:

    public class ManagerRoleAct : IManagerRoleAct
    {

        public IManager ActAs(User user)
        {
            if (!user.Roles.Contains("Manager"))
            {
                throw new Exception("当前用户不是Manager");
            }
            IManager role = new Manager(user);
            return role;
        }
    }

Employee的实现同上。

 

进一步,我们引入服务定位模式:

此时为了保证聚合根的纯净,我们增加领域服务:

    public class UserDomainService
    {

        /// <summary>
        /// 扮演角色
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="user"></param>
        /// <returns></returns>
        public T ActAs<T>(User user) where T : IRole
        {
            IRoleAct<T> act = (IRoleAct<T>)ServiceLocator.Current.GetService(typeof(IRoleAct<T>));
            var obj = act.ActAs(user);
            return (T)obj;
        }
    }

 

引入IOC容器(容器使用Unity4),让容器帮我们自动扫描所有的角色和工厂,并注册到容器里面:

class Program
    {


        static void Main(string[] args)
        {
         
            UnityContainer container = new UnityContainer();
            UnityServiceLocator locator = new UnityServiceLocator(container);
            ServiceLocator.SetLocatorProvider(() => locator);

            Assembly assembly = Assembly.GetAssembly(typeof(Program));
            List<Type> types = assembly
                .GetTypes()
                .Where(s=> s.IsClass && s.GetInterfaces().Any(t=>t.Name.Contains("IRoleAct")))
                .ToList();

            foreach (var item in types)
            {
                foreach (var itemInterface in item.GetInterfaces())
                {
                    container.RegisterType(itemInterface, item);
                }
            }

            Console.ReadLine();
        }

  
    }

 看看应用层的调用:

        private static void TestUserRole()
        {
            User managerUser = new User();
            managerUser.Id = Guid.NewGuid().ToString("N");
            managerUser.Name = "张三经理";
            managerUser.Roles.Add("Manager");


            User employeeUser = new User();
            employeeUser.Id = Guid.NewGuid().ToString("N");
            employeeUser.Name = "李四经理";
            employeeUser.Roles.Add("Employee");


            UserDomainService service = new UserDomainService();


            IEmployee employee = service.UserActAs<IEmployee>(employeeUser);
            ApplyOrder order = employee.ApplyNewOrder("请病假");


            IManager manager = service.UserActAs<IManager>(managerUser);
            manager.ApprovalOrder(order);

        }

使用DCI

我们引入DCI,将审批动作放入场景里面:

    public class ApplyOrderApprovalContext
    {

        public ApplyOrderApprovalContext(User user,ApplyOrder order)
        {
            this.User = user;
            this.Order = order;
        }


        public User User { get; private set; }


        public ApplyOrder Order { get; private set; }



        public void Interaction()
        {
            UserDomainService service = new UserDomainService();
            IManager manager = service.UserActAs<IManager>(User);
            manager.ApprovalOrder(Order);
        }

    }

欢迎大家讨论我上述设计的优缺点!

最后:特别感谢汤神的博客dax.net的博客,还有园里的蟋蟀腾飞等大大们的领域驱动设计相关的文章。

DCI分析参考汤神的文章:http://www.cnblogs.com/netfocus/archive/2011/09/18/2180656.html

探讨DDD中角色权限与DCI的使用

标签:

原文地址:http://www.cnblogs.com/zhengqzheng/p/5785286.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!