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类:接口类,用于约束数据库访问类的方法示例代码:
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调用,具有承上启下的作用:

示例代码:

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。(详细见下文数据库处理类的方法)

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

示例代码:
 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:

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:

class UserRequest:

    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password = password

  Response:

class UserResponse:

    def __init__(self, status=True, message=‘‘, model_view=None):
        self.status = status    # 是否登陆成功的状态
        self.message = message  #错误信息
        self.modelView = model_view  #登陆成功后的用户数据 
UserService:
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的调用者:

示例代码:

from ..Core.HttpRequest import AdminRequestHandler
from Service.User.Service import UserService
class Foo:#便于前端使用属性获取数据
    pass
class Login(AdminRequestHandler):

    def get(self, *args, **kwargs):
        user_request=Foo()
        #获取用户输入的用户名邮箱密码,并封装进user_request对象
        user_request.name=self.get_argument(‘name‘,None)
        user_request.email=self.get_argument(‘email‘,None)
        user_request.password=self.get_argument(‘password‘,None)
        obj=UserService.check_login(user_request)#获取服务端Service的返回值
        self.render(‘Account/Login.html‘,obj=obj) #传入前端,并渲染

    def post(self, *args, **kwargs):
        pass

  

  总结以上就是基于领域驱动模型的用户管理后台,包含数据库操作文件,数据库业务处理文件,服务端文件,UI层文件。如果本文对您有参考价值,欢迎帮博主点下文章下方的推荐,谢谢!

   
时间: 2024-08-02 17:46:44

tornado项目之基于领域驱动模型架构设计的京东用户管理后台的相关文章

【tornado】系列项目(二)基于领域驱动模型的区域后台管理+前端easyui实现

本项目是一个系列项目,最终的目的是开发出一个类似京东商城的网站.本文主要介绍后台管理中的区域管理,以及前端基于easyui插件的使用.本次增删改查因数据量少,因此采用模态对话框方式进行,关于数据量大采用跳转方式修改,详见博主后续博文. 后台界面展示: 地区管理包含省市县的管理.详见下文. 一.数据库设计 ? 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 27 class Province(Base):  

新年第一弹:基于领域驱动的Java开发工具包Common项目分享

项目地址:https://github.com/xuliugen/common 喜欢的请加星. 先上图: 本项目主要包含:constant.domain.exception.util这四个主要部分. Constant 主要包含了一些项目中的常用常量. 示例: (1)标点符号常量类 public interface ConstPunctuation { /** * : 冒号 */ String COLON = ":"; /** * - 中划线 */ String MINUS = &quo

领域驱动模型和以数据为中心模型的一些思考

假设有如下业务逻辑: 我要搬到新地方去工作. 代码1: class Player { public: void setAddr(string name); void setJob(string job); private: string _name; string _job; } 代码2: class Player { public: void changeAddr(string name); void changeJob(string job); private: string _name;

【译文】一个设计精良的领域驱动模型的五个特征

查看原文 我在这篇博客文章中,我试图给领域模型下一个非常合适的定义,我发现我的这些定义都不太妥当,不过,我们还是可以先来看一下wiki百科对领域驱动模型下的定义: 问题解决和软件工程中的领域模型可以被认为是感兴趣的领域(通常称为问题领域)的概念模型,其描述了各种实体,它们的属性和关系,以及控制完整性的约束.包含该问题域的模型元素. 听起来很重要?换句话说,领域模型是每个应用程序的重要组成部分,它是现实世界概念的表示.但是,如何区分好的领域模型和坏的模型呢?由于了解领域模型,你也需要了解问题域,因

架构风格与基于网络应用软件的架构设计(一)

作者:Roy Thomas Fielding 译者:李锟 论文摘要 Web(万维网,英文全称World Wide Web,简称Web)的成功,很大程度上是因为其软件架构的设计满足了拥有互联网规模(Internet-scale)的分布式超媒体系统的需求.在过去10年间,通过对定义Web架构的规范所做的一系列修改,Web以迭代的方式不断地发展着.为了识别出Web需要改善的那些方面,并且避免对其进行不必要的修改,需要一种现代的Web架构模型,用来指导Web的设计.定义和部署. 软件架构方面的研究探索的

基于platform驱动模型的LED驱动

上一篇博文<platform设备驱动框架搭建分析>主要是根据内核源码来分析platform驱动模型工作的原理,在实际的驱动开发中如何使用Linux的这么一种模型来管理这种类型的设备呢?把tq2440开发板上的LED1当做是平台设备注册到Linux系统中,让系统可以用这种platform驱动来管理他. ①总线层:代码不用我们自己去写,内核已经提供了 ②设备层:向platform总线层注册硬件相关的资源,一般是寄存器地址.内存空间.中断号(序号的一种代表)等等 led_dev.c #include

linux驱动(九)platform驱动模型详解,以及基于platform驱动模型的led驱动

参考: http://blog.csdn.net/qq_28992301/article/details/52385518 http://blog.csdn.net/zoe6553/article/details/6372445 http://blog.chinaunix.net/uid-25014876-id-111745.html 1:什么是platform总线?platform总线是区别于实体总线USB. I2C.SPI .PIC总线的虚拟总线,一些usb设备选址的话需要通过USB总线来进

tornado项目

tornado项目之基于领域驱动模型架构设计的京东用户管理后台 本博文将一步步揭秘京东等大型网站的领域驱动模型,致力于让读者完全掌握这种网络架构中的“高富帅”. 一.预备知识: 1.接口: python中并没有类似java等其它语言中的接口类型,但是python中有抽象类和抽象方法.如果一个抽象类有抽象方法,那么继承它的子类必须实现抽象类的所有方法,因此,我们基于python的抽象类和抽象方法实现接口功能. 示例代码:  接口示例代码 2.依赖注入: 依赖注入的作用是将一系列无关的类通过注入参数

基于吉日嘎底层架构的Web端权限管理操作演示-角色管理

上一篇介绍了用户管理,这篇来介绍角色管理,这是权限管理的核心部分,因为我们的权限管理系统是基于角色的,有个高大上的英文名叫RBAC(Role Based Acccess Control). 下面的这段演示视频,会介绍角色管理的如下功能: 新增.修改.锁定.解锁.删除.撤销删除 角色成员管理 角色菜单模块管理 导出角色数据 优酷视频地址,手机无法播放的点击这里:http://v.youku.com/v_show/id_XMTc0MjU1NTcxMg==.html?f=28263687&o=1 下一