Java 中的领域模型模式:构建健壮的业务逻辑
也称为
- 概念模型
- 领域对象模型
领域模型设计模式的意图
领域模型模式旨在在您的软件中创建一个概念模型,该模型与它旨在表示的现实世界系统相匹配。它涉及使用丰富的领域对象,这些对象封装了与应用程序领域相关的 数据和行为,确保业务逻辑集中化。
领域模型模式的详细解释,包括实际例子
现实世界中的例子
考虑一个使用领域模型设计模式的在线书店系统。在这个系统中,各种领域对象,例如 `Book`、`Author`、`Customer` 和 `Order`,封装了核心业务逻辑和规则。例如,`Book` 对象包含标题、作者、价格和库存数量等属性,以及用于管理这些属性的方法。`Order` 对象管理订单详细信息,计算总价并验证库存可用性。这种方法确保业务逻辑集中在领域对象内,使系统更加模块化、易于维护和可扩展,以便添加新功能。
通俗地说
领域模型是域的 对象模型,它包含行为和数据。
Java 中领域模型模式的编程示例
假设我们需要构建一个电子商务网站应用程序。在分析需求时,您会注意到您反复提到一些名词。例如,您的客户,以及客户寻找的产品。这两个是您特定于域的类,每个类都将包含一些特定于其域的业务逻辑。
在电子商务应用程序的例子中,我们需要处理客户购买产品和退货的域逻辑。我们可以使用领域模型模式并创建 `Customer` 和 `Product` 类,其中每个类的实例都包含行为和数据,并且只表示底层表中的一个记录。
public class Customer {
// Customer properties and methods
}
public class Product {
// Product properties and methods
}
数据访问对象 (DAO):这些对象为数据库提供抽象接口。它们用于检索领域实体并将更改保存回数据库。在提供的代码中,`CustomerDaoImpl` 和 `ProductDaoImpl` 是 DAO。
public class CustomerDaoImpl implements CustomerDao {
// Implementation of the methods defined in the CustomerDao interface
}
public class ProductDaoImpl implements ProductDao {
// Implementation of the methods defined in the ProductDao interface
}
域逻辑:它封装在域实体内。例如,`Customer` 类具有 `buyProduct` 和 `returnProduct` 等方法,这些方法表示客户可以执行的操作。
public class Customer {
// Other properties and methods...
public void buyProduct(Product product) {
// Implementation of buying a product
}
public void returnProduct(Product product) {
// Implementation of returning a product
}
}
应用程序:`App` 类使用域实体及其方法来实现应用程序的业务逻辑。
public class App {
public static final String H2_DB_URL = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
public static final String CREATE_SCHEMA_SQL =
"CREATE TABLE CUSTOMERS (name varchar primary key, money decimal);"
+ "CREATE TABLE PRODUCTS (name varchar primary key, price decimal, expiration_date date);"
+ "CREATE TABLE PURCHASES ("
+ "product_name varchar references PRODUCTS(name),"
+ "customer_name varchar references CUSTOMERS(name));";
public static final String DELETE_SCHEMA_SQL =
"DROP TABLE PURCHASES IF EXISTS;"
+ "DROP TABLE CUSTOMERS IF EXISTS;"
+ "DROP TABLE PRODUCTS IF EXISTS;";
public static void main(String[] args) throws Exception {
// Create data source and create the customers, products and purchases tables
final var dataSource = createDataSource();
deleteSchema(dataSource);
createSchema(dataSource);
// create customer
var customerDao = new CustomerDaoImpl(dataSource);
var tom =
Customer.builder()
.name("Tom")
.money(Money.of(USD, 30))
.customerDao(customerDao)
.build();
tom.save();
// create products
var productDao = new ProductDaoImpl(dataSource);
var eggs =
Product.builder()
.name("Eggs")
.price(Money.of(USD, 10.0))
.expirationDate(LocalDate.now().plusDays(7))
.productDao(productDao)
.build();
var butter =
Product.builder()
.name("Butter")
.price(Money.of(USD, 20.00))
.expirationDate(LocalDate.now().plusDays(9))
.productDao(productDao)
.build();
var cheese =
Product.builder()
.name("Cheese")
.price(Money.of(USD, 25.0))
.expirationDate(LocalDate.now().plusDays(2))
.productDao(productDao)
.build();
eggs.save();
butter.save();
cheese.save();
// show money balance of customer after each purchase
tom.showBalance();
tom.showPurchases();
// buy eggs
tom.buyProduct(eggs);
tom.showBalance();
// buy butter
tom.buyProduct(butter);
tom.showBalance();
// trying to buy cheese, but receive a refusal
// because he didn't have enough money
tom.buyProduct(cheese);
tom.showBalance();
// return butter and get money back
tom.returnProduct(butter);
tom.showBalance();
// Tom can buy cheese now because he has enough money
// and there is a discount on cheese because it expires in 2 days
tom.buyProduct(cheese);
tom.save();
// show money balance and purchases after shopping
tom.showBalance();
tom.showPurchases();
}
private static DataSource createDataSource() {
var dataSource = new JdbcDataSource();
dataSource.setUrl(H2_DB_URL);
return dataSource;
}
private static void deleteSchema(DataSource dataSource) throws SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(DELETE_SCHEMA_SQL);
}
}
private static void createSchema(DataSource dataSource) throws SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(CREATE_SCHEMA_SQL);
}
}
}
程序输出
12:17:23.834 [main] INFO com.iluwatar.domainmodel.Customer -- Tom balance: USD 30.00
12:17:23.836 [main] INFO com.iluwatar.domainmodel.Customer -- Tom didn't bought anything
12:17:23.841 [main] INFO com.iluwatar.domainmodel.Customer -- Tom want to buy Eggs($10,00)...
12:17:23.842 [main] INFO com.iluwatar.domainmodel.Customer -- Tom bought Eggs!
12:17:23.842 [main] INFO com.iluwatar.domainmodel.Customer -- Tom balance: USD 20.00
12:17:23.842 [main] INFO com.iluwatar.domainmodel.Customer -- Tom want to buy Butter($20,00)...
12:17:23.843 [main] INFO com.iluwatar.domainmodel.Customer -- Tom bought Butter!
12:17:23.843 [main] INFO com.iluwatar.domainmodel.Customer -- Tom balance: USD 0.00
12:17:23.843 [main] INFO com.iluwatar.domainmodel.Customer -- Tom want to buy Cheese($20,00)...
12:17:23.843 [main] ERROR com.iluwatar.domainmodel.Customer -- Not enough money!
12:17:23.843 [main] INFO com.iluwatar.domainmodel.Customer -- Tom balance: USD 0.00
12:17:23.843 [main] INFO com.iluwatar.domainmodel.Customer -- Tom want to return Butter($20,00)...
12:17:23.844 [main] INFO com.iluwatar.domainmodel.Customer -- Tom returned Butter!
12:17:23.844 [main] INFO com.iluwatar.domainmodel.Customer -- Tom balance: USD 20.00
12:17:23.844 [main] INFO com.iluwatar.domainmodel.Customer -- Tom want to buy Cheese($20,00)...
12:17:23.844 [main] INFO com.iluwatar.domainmodel.Customer -- Tom bought Cheese!
12:17:23.846 [main] INFO com.iluwatar.domainmodel.Customer -- Tom balance: USD 0.00
12:17:23.846 [main] INFO com.iluwatar.domainmodel.Customer -- Tom bought: Eggs - $10.00, Cheese - $20.00
领域模型模式的详细解释,包括实际例子
何时在 Java 中使用领域模型模式
- 适用于具有丰富业务逻辑的复杂应用程序。
- 当业务逻辑或域复杂度很高,需要一个与现实世界实体及其关系密切匹配的模型时。
- 适用于域专家参与开发过程以确保模型准确反映域概念的应用程序。
领域模型模式 Java 教程
Java 中领域模型模式的实际应用
- 企业应用程序 (ERP、CRM 系统)
- 金融系统 (银行、交易平台)
- 医疗保健应用程序 (患者记录管理)
- 电子商务平台 (产品目录、购物车)
领域模型模式的优势和权衡
优点
- 改进的沟通:为开发人员和域专家提供通用语言,增强理解和协作。
- 灵活性:将业务逻辑封装在域实体中,使其更容易修改和扩展,而不会影响系统的其他部分。
- 可维护性:结构良好的领域模型可以简化应用程序的维护和演变。
- 可重用性:域类通常可以在同一域内的不同项目中重用。
权衡
- 复杂性:可能会引入复杂性,尤其是在简单的应用程序中,领域模型可能过于复杂。
- 性能问题:具有复杂行为的丰富领域对象可能会导致性能瓶颈,需要仔细优化。
- 学习曲线:需要对域有很好的理解,对于不熟悉域概念的开发人员来说,可能需要较高的学习曲线。
相关的 Java 设计模式
- 数据访问对象 (DAO):用于抽象和封装所有对数据源的访问。
- 仓库:在域和数据映射层之间进行协调,充当内存中域对象集合。
- 服务层:定义应用程序与服务层的边界,该层建立了一组可用的操作并协调应用程序在每个操作中的响应。
- 工作单元:维护受业务事务影响的对象列表并协调更改的写入。