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

一、预备知识:

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,"")
Mapper.regist(Bar,"") 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的调用者:

示例代码:

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层文件。如果本文对您有参考价值,欢迎帮博主点下文章下方的推荐,谢谢!

   

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

  1. 【Python之路】特别篇--基于领域驱动模型架构设计的京东用户管理后台

    一.预备知识: 1.接口: - URL形式 - 数据类型 (Python中不存在) a.类中的方法可以写任意个,想要对类中的方法进行约束就可以使用接口: b.定义一个接口,接口中定义一个方法f1: c ...

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

    本项目是一个系列项目,最终的目的是开发出一个类似京东商城的网站.本文主要介绍后台管理中的区域管理,以及前端基于easyui插件的使用.本次增删改查因数据量少,因此采用模态对话框方式进行,关于数据量大采 ...

  3. [.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店

    一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Byteart Retail案例并没有对其形成过程做一步步分析,而是把整个DDD的实现案例展现给我们,这 ...

  4. [.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结

    一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计 ...

  5. 基于领域驱动设计(DDD)超轻量级快速开发架构(二)动态linq查询的实现方式

    -之动态查询,查询逻辑封装复用 基于领域驱动设计(DDD)超轻量级快速开发架构详细介绍请看 https://www.cnblogs.com/neozhu/p/13174234.html 需求 配合Ea ...

  6. 领域驱动模型DDD(二)——领域事件的订阅/发布实践

    前言 凭良心来说,<微服务架构设计模式>此书什么都好,就是选用的业务过于庞大而导致代码连贯性太差,我作为读者来说对于其中采用的自研框架看起来味同嚼蜡,需要花费的学习成本实在是过于庞大,不仅 ...

  7. 领域驱动模型DDD(一)——服务拆分策略

    前言 领域驱动模型设计在业界也喊了几年口号了,但是对于很多"务实"的程序员来说,纸上谈"术"远比敲代码难得太多太多.本人能力有限,在拜读相关作品时既要隐忍书中晦 ...

  8. 基于领域驱动设计(DDD)超轻量级快速开发架构

    smartadmin.core.urf 这个项目是基于asp.net core 3.1(最新)基础上参照领域驱动设计(DDD)的理念,并参考目前最为了流行的abp架构开发的一套轻量级的快速开发web ...

  9. 基于吉日嘎底层架构的Web端权限管理操作演示-用户管理

    相信博客园的朋友对吉日嘎拉都不陌生,相信很多人也买了他的源码,应用于自己的项目. 但是你有没有过一个困惑? 那就是:没有一个基于网页的权限管理界面. 今天,这一切都不再是问题,我花了3年时间研究学习并 ...

随机推荐

  1. BestCoder Round #61 1002 Game

    Problem Description XY is playing a game:there are N pillar in a row,which numbered from 1 to n.Each ...

  2. &#65279导致页面顶部空白一行解决方法

    模板文件生成html文件之后会在body开头处加入一个可见的控制符&#65279,导致页面头部会出现一个空白行.原因是页面的编码是UTF-8 + BOM. 这种编码方式一般会在windows操 ...

  3. Dom终

    l创建DOM元素 •createElement(标签名)  创建一个节点 •appendChild(节点)  追加一个节点 –例子:为ul插入li <!DOCTYPE html PUBLIC & ...

  4. PHP程序员如何突破成长瓶颈(php开发三到四年)

    看了这篇博文,我正好处于这个阶段,也有心要突破自己,呵呵! 作为Web开发中应用最广泛的语言之一,PHP有着大量的粉丝,那么你是一名优秀的程序员吗?在进行自我修炼的同时,你是否想过面对各种各样的问题, ...

  5. 【MySQL】MySQL 如何实现 唯一随机数ID

    如果不是 UUID 好像比较困难 参考资料: http://bbs.csdn.net/topics/390001507 https://www.zhihu.com/question/20151242

  6. centos vim配置高亮语法和格式化粘贴

    centos vim配置高亮语法和格式化粘贴 设置vim别名和高亮grep词语 echo -e "\nalias vi=vim\nalias grep='grep --color'\n&qu ...

  7. ios xib 中的 size class

    需要阅读UITraitCollection的说明文档,先截图如下: 今天说说xib中的size class的简单设置,先看图 一共有9个小块,水平方向代表width,垂直方向代表height. 对于w ...

  8. ffmpeg-20160522-git-bin

    ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...

  9. Debian 配置apt-get源

    1.配置apt-get源 cp  /etc/apt/sources.list  /etc/apt/sources.listbak   #备份原有配置文件       nano  /etc/apt/so ...

  10. Ubuntu 配置 Tomcat

    系统环境:Ubuntu 10.10(linux-kernel 2.6.35-22) 安装版本:apache-tomcat-7.0.29.tar.gz(官方网址:Apache Tomcat) 安装步骤: ...