• 【tornado】系列项目(一)之基于领域驱动模型架构设计的京东用户管理后台


    【tornado】系列项目(一)之基于领域驱动模型架构设计的京东用户管理后台

     

       本博文将一步步揭秘京东等大型网站的领域驱动模型,致力于让读者完全掌握这种网络架构中的“高富帅”。

    一、预备知识:

    1.接口:

    python中并没有类似java等其它语言中的接口类型,但是python中有抽象类和抽象方法。如果一个抽象类有抽象方法,那么继承它的子类必须实现抽象类的所有方法,因此,我们基于python的抽象类和抽象方法实现接口功能。

    示例代码:

    from abc import ABCMeta
    from abc import abstractmethod #导入抽象方法
    
    class Father(metaclass=ABCMeta):#创建抽象类
        @abstractmethod
        def f1(self):pass
        @abstractmethod
        def f2(self):pass
    
    class F1(Father):
        def f1(self):pass
        def f2(self):pass
        def f3(self):pass
    obj=F1()
    接口示例代码

    2.依赖注入:

    依赖注入的作用是将一系列无关的类通过注入参数的方式实现关联,例如将类A的对象作为参数注入给类B,那么当调用类B的时候,类A会首先被实例化。

    示例代码:

    class Mapper:
        __mapper_ralation={}
        @staticmethod
        def regist(cls,value):
            Mapper.__mapper_ralation[cls]=value
    
        @staticmethod
        def exist(cls):
            if cls in Mapper.__mapper_ralation:
                return True
            else:
                return False
        @staticmethod
        def value(cls):
            return Mapper.__mapper_ralation[cls]
    
    class Mytype(type):
        def __call__(cls, *args, **kwargs):
            obj=cls.__new__(cls, *args, **kwargs)
            arg_list=list(args)
            if Mapper.exist(cls):
                value=Mapper.value(cls)
                arg_list.append(value)
            obj.__init__(*arg_list)
            return obj
    
    class Foo(metaclass=Mytype):
        def __init__(self,name):
            self.name=name
    
        def f1(self):
            print(self.name)
    class Bar(metaclass=Mytype):
        def __init__(self,name):
            self.name=name
    
        def f1(self):
            print(self.name)
    
    Mapper.regist(Foo,"666")
    Mapper.regist(Bar,"999")
    
    f=Foo()
    print(f.name)
    b=Bar()
    print(b.name)
    依赖注入示例

    注:原理:首先需要明确一切事物皆对象类也是对象,类是有Type创建的,当类实例化的时候,会调用type类的call方法,call方法会调用new方法,new方法调用init方法。

    二、企业级应用设计

    1.总体框架目录结构:

    备注:

    • Infrastructure:一些公共组件,例如md5加密,分页模块,session等。
    • Model :关于数据库的逻辑处理模块
    • Repository :数据访问层,包含数据库的增删改查
    • Service :服务层,调用Model,包含带有规则的请求和返回
    • Statics:静态文件目录
    • UI层:业务处理
    • Views:模板文件
    • Application:tornado程序的起始文件
    • Config:配置文件
    • Mapper:依赖注入文件,负责整个框架不同类的依赖注入

    2.首先我们从Moldel开始查看:

    文件目录:

    本文主要以用户管理为例进行介绍,因此我们来关注一下User.py文件:

    代码结构:

    下面对上述代码结构做一一介绍:

    IUseRepository类:接口类,用于约束数据库访问类的方法
    示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class IUseRepository:
        """
        用户信息仓库接口
        """
     
        def fetch_one_by_user_pwd(self, username, password):
            """
            根据用户名密码获取模型对象
            :param username: 主键ID
            :param password: 主键ID
            :return:
            """
        def fetch_one_by_email_pwd(self, email, password):
            """
            根据邮箱密码获取模型对象
            :param email: 主键ID
            :param password: 主键ID
            :return:
            """
     
        def update_last_login_by_nid(self,nid,current_date):
            """
            根据ID更新最新登陆时间
            :param nid:
            :return:
            """

      从上述代码可以看出,数据库访问类如果继承IUseRepository类,就必须实现其中的抽象方法。

    接下来的三个类,VipType、UserType、User是与用户信息相关的类,是数据库需要保存的数据,我们希望存入数据库的数据格式为:nid 、username、email、last_login、user_type_id、vip_type_id,其中User类用于保存上述数据。因为user_type_id、vip_type_id存的是数字,即user_type_id、vip_type_id是外键,不能直接在前端进行展示,因此,我们创建了VipType、UserType类,用于根据id,获取对应的VIP级别和用户类型。

    示例代码:

    class User:
        """领域模型"""
        def __init__(self, nid, username, email, last_login, user_type, vip_type):
            self.nid = nid
            self.username = username
            self.email = email
            self.last_login = last_login
            self.user_type = user_type
            self.vip_type = vip_type
    User类
    class UserType:
    
        USER_TYPE = (
            {'nid': 1, 'caption': '用户'},
            {'nid': 2, 'caption': '商户'},
            {'nid': 3, 'caption': '管理员'},
        )
    
        def __init__(self, nid):
            self.nid = nid
    
        def get_caption(self):
            caption = None
    
            for item in UserType.USER_TYPE:
                if item['nid'] == self.nid:
                    caption = item['caption']
                    break
            return caption
    
        caption = property(get_caption)
    UserType类
    class VipType:
    
        VIP_TYPE = (
            {'nid': 1, 'caption': '铜牌'},
            {'nid': 2, 'caption': '银牌'},
            {'nid': 3, 'caption': '金牌'},
            {'nid': 4, 'caption': '铂金'},
        )
    
        def __init__(self, nid):
            self.nid = nid
    
        def get_caption(self):
            caption = None
    
            for item in VipType.VIP_TYPE:
                if item['nid'] == self.nid:
                    caption = item['caption']
                    break
            return caption
    
        caption = property(get_caption)
    VipType类

    注:VipType、UserType这两个类获取对应的caption均是通过类的普通特性访问,即类似字段方式访问。

    接下来的类UserService是本py文件的重中之重,它负责调用对应的数据库访问类的方法,并被服务层service调用,具有承上启下的作用:

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class UserService:
     
        def __init__(self, user_repository):
            self.userRepository = user_repository
     
        def check_login(self, username=None, email=None, password=None):
     
            if username:
                user_model = self.userRepository.fetch_one_by_user_pwd(username, password)
            else:
                user_model = self.userRepository.fetch_one_by_email_pwd(email, password)
            if user_model:
                current_date = datetime.datetime.now()
                self.userRepository.update_last_login_by_nid(user_model.nid, current_date)
            return user_model

      这里,我们重点介绍一下上述代码:

    初始化参数user_repository:通过依赖注入对应的数据库访问类的对象;

    check_login:访问数据库的关键逻辑处理方法,根据用户是用户名+密码方式还是邮箱加密码的方式登录,然后调用对应的数据库处理方法,如果登陆成功,更新时间和最后登录时间,最后将User类的实例返回给调用它的服务层service。(详细见下文数据库处理类的方法)

    我们先来看一下对应的数据库处理类中的一个方法:

    1
    示例代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    def fetch_one_by_user_pwd(self, username, password):
           ret = None
           cursor = self.db_conn.connect()
           sql = """select nid,username,email,last_login,vip,user_type from UserInfo where username=%s and password=%s"""
           cursor.execute(sql, (username, password))
           db_result = cursor.fetchone()
           self.db_conn.close()
     
           if db_result:
               ret = User(nid=db_result['nid'],
                          username=db_result['username'],
                          email=db_result['email'],
                          last_login=db_result['last_login'],
                          user_type=UserType(nid=db_result['user_type']),
                          vip_type=VipType(nid=db_result['vip'])
                          )
           return ret

      这里我们使用pymysql进行数据库操作,以用户名+密码登陆为例,如果数据库有对应的用户名和密码,将查询结果放在User类中进行初始化,至此,ret中封装了用户的全部基本信息,将ret返回给上面的check_login方法,即对应上文中的返回值user_model,user_model返回给调用它的服务层service。

    总结:Molde最终将封装了用户基本信息的User类的实例返回给服务层service。

    3.接下来我们看一下服务层service:

    service也是一个承上启下的作用,它调用Moldel文件对应的数据库业务协调方法,并被对应的UI层调用(本例中是UIadmin)。

    目录结构:

    同样的,我们只介绍User文件夹:它包含4个py文件:

    • ModelView:用于用户前端展示的数据格式化,重点对user_type_id、vip_type_id增加对应的用户类型和VIP级别,即将外键数据对应的caption放在外键后面,作为增加的参数。
    • Request:封装请求Moldel文件对应数据库协调类的参数
    • Response:封装需要返回UI层的参数
    • Sevice:核心服务处理文件

    下面对上述目录做详细代码:

    ModelView:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class UserModelView:
     
        def __init__(self, nid, username, email, last_login, user_type_id, user_type_caption, vip_type_id, vip_type_caption):
            self.nid = nid
            self.username = username
            self.email = email
            self.last_login = last_login
            self.user_type = user_type_id
            self.user_type_caption = user_type_caption
            self.vip_type = vip_type_id
            self.vip_type_caption = vip_type_caption

      注:对user_type_id、vip_type_id增加对应的用户类型和VIP级别,即将外键数据对应的caption放在外键后面,作为增加的参数。

    Request:

    1
    2
    3
    4
    5
    6
    class UserRequest:
     
        def __init__(self, username, email, password):
            self.username = username
            self.email = email
            self.password = password

      Response:

    1
    2
    3
    4
    5
    6
    class UserResponse:
     
        def __init__(self, status=True, message='', model_view=None):
            self.status = status    # 是否登陆成功的状态
            self.message = message  #错误信息
            self.modelView = model_view  #登陆成功后的用户数据 
    UserService:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    class UserService:
     
        def __init__(self, model_user_service):   #通过依赖注入Moldel对应的数据库业务协调方法,此例中对应上文中的user_service
            self.modelUserService = model_user_service  
     
        def check_login(self, user_request):  #核心服务层业务处理方法
            response = UserResponse()  #实例化返回类
     
            try:
                model = self.modelUserService.check_login(user_request.username, user_request.email, user_request.password) #接收上文中的用户基本信息,是User类的实例
                if not model:
                    raise Exception('用户名或密码错误')
                else:   #如果登陆成功,通过UserModelView类格式化返回前端的数据
                    model_view = UserModelView(nid=model['nid'],
                                               username=model['usename'],
                                               email=model['email'],
                                               last_login=model['last_login'],
                                               user_type_id=model['user_type'].nid,
                                               user_type_caption=model['user_type'].caption,
                                               vip_type_id=model['vip_type'].nid,
                                               vip_type_caption=model['vip_type'].caption,)
                    response.modelView = model_view    #定义返回UI层的用户信息
            except Exception as e:
                response.status = False
                response.message = str(e)

      总结:至此,Service返回给Ui层的数据是是否登陆成功的状态status、错误信息、已经格式化的用户基本信息。

    4.UI层

    UI层主要负责业务处理完成后与前端的交互,它是服务层Service的调用者:

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Login(AdminRequestHandler):
     
        def get(self, *args, **kwargs):
            is_login=self.session["is_login"]
            current_user=""
            if is_login:
                current_user=self.session["user_info"].username
            self.render('Account/Login.html',current_user=current_user)
     
        def post(self, *args, **kwargs):
            user_request=[]
            #获取用户输入的用户名邮箱密码
            user_request.append(self.get_argument('name',None))
            user_request.append(self.get_argument('email',None))
            user_request.append(self.get_argument('pwd',None))
            checkcode=self.get_argument("checkcode",None)
            Mapper.mapper(*user_request)
            obj=UserService.check_login()
            self.session["is_login"]=True
            self.session["user_info"]=obj.modelView
            self.write("已登陆")

         总结以上就是基于领域驱动模型的用户管理后台,包含数据库操作文件,数据库业务处理文件,服务端文件,UI层文件。

       
     
     
     
  • 相关阅读:
    使用Docker Swarm搭建分布式爬虫集群
    如果你不知道做什么,那就学一门杂学吧
    正则表达式re.sub替换不完整的问题现象及其根本原因
    Visual Studio 2019 正式版今日发布 key
    net core 记录自定义端口多个方式
    HTTP Error 500.0
    来自后端的逆袭 blazor简介 全栈的福音
    创建一个RAS 非对称 公私密钥示例
    树莓派安装window ioT
    WPF USB设备采集开源工具介绍
  • 原文地址:https://www.cnblogs.com/zcok168/p/10090755.html
Copyright © 2020-2023  润新知