领域驱动设计

2019-08-02  本文已影响0人  gregoriusxu

在经过许多年的复制粘贴工作之后,你或许会发现编程也不过如此,但是考虑到微薄的工资迟迟没有上涨的趋势,那么你是否应该做出人生的一个重要选择了,是继续这么复制粘贴下去还是扬尘而去,不带走程序界的一篇云彩,挥一挥衣袖,就像曾经重来没有来过一样;其实,不然;你可以有第三个选择,是时候该考虑一下提升的问题了,我们称之为“设计”。

大多数的程序员从事的与企业应用相关的事业,所以大部分人都需要熟悉前端,后端相关的知识。作为一名后端开发人员,数据库与我们的系统息息相关,所以我们写的最多的还是操作数据库的代码,下面以java操作数据库的示例开始展开领域驱动设计:

package service;

/**
* 对应数据库在中的tb_user表
*/
public class User {
    
    private int id;
    
    private String name;
    
    private String pwd;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}


package service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import util.DBHelper;

public class dao{

    // 数据库连接对象,注意:是导入“java.sql.Connection”此包下的连接对象。
    private static Connection conn = null; 

    // PreparedStatement对象用来执行SQL语句
    private static PreparedStatement pst = null; 

    //结果集
    private static ResultSet rs = null;

    //保存查询到的实体集合
    List list = null;

    /**
    * 查询数据库,获取一个User类型的list集合
    */
    public List getUsers(){
        list = new ArrayList<User>();
        try {
            conn=DBHelper.getConnection();  //从DBHelper获取连接对象
            // 创建statement 执行数据库语句,第一不预处理,第二步才是正式。执行的语句可以修改.
            // 不过为了防止SQl注入。骗取登录,所以最好创建它的子类PreparedStatement 
            pst = conn.prepareStatement("SELECT * FROM users");// 预处理
            rs = pst.executeQuery();// 这里才是执行,获得数据。 

            // 遍历处理结果集,用while 方法next方法,相当于指针依次下移,获得每一行表的数据 
            while (rs.next()) {
                User user=new User(); 
                /**
                * 两种get,
                * 一种是根据下标 从1开始每一列, 
                * rs.getInt(1); 
                * rs.getString(2); 
                * rs.getString(3);
                * 第二个是根据列名查找。根据表的列数据类型使用不同的个体方法 
                */

                //为user属性赋值
                user.setId(rs.getInt(1));
                user.setName(rs.getString(2));
                user.setPwd(rs.getString(3)); 

                //将user对象添加到list集合中
                list.add(user); 
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (rs != null) 
                    rs.close(); 
                if (pst != null) 
                    pst.close(); 
                /*if (conn != null) 
                    conn.close(); */    //注意:如果后续还要使用connection,则不用关闭
            } catch (SQLException e) 
            { 
                e.printStackTrace(); 
            }
        }
        return list;
    }
}

基本上每个人都写过上面的代码,上面的示例很简单,就是连接mysql数据库获取人员数据信息。如果你想获取部门相关的数据,那么你肯定会开始复制粘贴了,然后将SELECT * FROM users改为SELECT * FROM depts。虽然这样可以实现业务功能,使上级主管和老板满意,但是有一天老板告诉你由于业务扩大,客户要求我们要改为redis存储数据,那么你上面的代码可以就需要全部重写了。那么怎么样在需求变动时我们写的代码不用修改或者很少修改就能适配需求的变化?首先我们列举一下以上代码的问题:

  1. 基础设施(jdbc)与业务(取数)耦合
  2. 以上代码不能如实反应出业务模型
  3. 代码缺少分层,我们在调整代码时整个jar包需要重新编译和打包

如果采用领域驱动设计,那么代码怎么写?请看示例:

User.java(DomainModel)

package service;

/**
* 对应数据库在中的tb_user表
*/
public class User {
    
    private int id;
    
    private String name;
    
    private String pwd;
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

IUserRepository.java(DomainCore)

package service;

import java.util.List;

public interface IUserRepository {
    List<User> getUsers();
}

UserService.java(Service)

package service;

import java.util.List;

public class UserService {
    IUserRepository userRepository;
    
    public UserService(IUserRepository userRepository) {
        super();
        this.userRepository = userRepository;
    }

    public List<User> getUsers()
    {
        return userRepository.getUsers();
    }
}

UserRepository.java(Infrastructure)

package service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import util.DBHelper;

public class UserRepository implements IUserRepository {

    // 数据库连接对象,注意:是导入“java.sql.Connection”此包下的连接对象。
    private static Connection conn = null;

    // PreparedStatement对象用来执行SQL语句
    private static PreparedStatement pst = null;

    // 结果集
    private static ResultSet rs = null;

    // 保存查询到的实体集合
    List list = null;

    @Override
    public List<User> getUsers() {
        list = new ArrayList<User>();
        try {
            conn = DBHelper.getConnection(); // 从DBHelper获取连接对象
            // 创建statement 执行数据库语句,第一不预处理,第二步才是正式。执行的语句可以修改.
            // 不过为了防止SQl注入。骗取登录,所以最好创建它的子类PreparedStatement
            pst = conn.prepareStatement("SELECT * FROM users");// 预处理
            rs = pst.executeQuery();// 这里才是执行,获得数据。

            // 遍历处理结果集,用while 方法next方法,相当于指针依次下移,获得每一行表的数据
            while (rs.next()) {
                User user = new User();
                /**
                * 两种get, 一种是根据下标 从1开始每一列, rs.getInt(1); rs.getString(2); rs.getString(3);
                * 第二个是根据列名查找。根据表的列数据类型使用不同的个体方法
                */

                // 为user属性赋值
                user.setId(rs.getInt(1));
                user.setName(rs.getString(2));
                user.setPwd(rs.getString(3));

                // 将user对象添加到list集合中
                list.add(user);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null)
                    rs.close();
                if (pst != null)
                    pst.close();
                /*
                * if (conn != null) conn.close();
                */ // 注意:如果后续还要使用connection,则不用关闭
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return list;
    }

}

那么以上代码有什么不同呢?

  1. 我们对代码进行了分层(Service,DomainCore,DomainModel,Infrastructure)
  2. 基础设施(Infrastructure)与业务(DomainCore)不再耦合
  3. 业务逻辑全部在DomainCore和DomainModel

这样做好处很明显,对于上面老板提的需求,我们只需要改动Infrastructure就可以实现,不需要改动其它层的逻辑。如果要获取用户所在部门信息,我们只需要修改DomainCore增加获取部门数据接口和实现DomainModel就可以了。

user.getDepts();

那么这样 User不再是简单的数据库的映射信息,它现在富有了“行为”,我们称之为“充血模型”。不再是之前的“贫血模型”,所以领域驱动的一个很重要的特征就是它是“充血模型”。

书山有路勤为径,学海无涯苦作舟。万里长征始于第一步,希望这篇文章给你很好的启发,欢迎在下面留言吐槽,且听下回分解。

上一篇 下一篇

猜你喜欢

热点阅读