Skip to content

Commit e498c25

Browse files
tao-sun2tao-sun2iluwatar
authored
feature: iluwatar#1319 add table module pattern (iluwatar#1742)
* modify table module pattern * fix code smells * resolve conversation Co-authored-by: tao-sun2 <sustc18st@gmai.com> Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
1 parent 122e6ed commit e498c25

File tree

11 files changed

+671
-0
lines changed

11 files changed

+671
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@
225225
<module>active-object</module>
226226
<module>model-view-viewmodel</module>
227227
<module>composite-entity</module>
228+
<module>table-module</module>
228229
<module>presentation</module>
229230
<module>lockable-object</module>
230231
</modules>

table-module/README.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
---
2+
layout: pattern
3+
title: Table Module
4+
folder: table-module
5+
permalink: /patterns/table-module/
6+
categories: Structural
7+
tags:
8+
- Data access
9+
---
10+
## Intent
11+
Table Module organizes domain logic with one class per table in the database, and a single instance of a class contains the various procedures that will act on the data.
12+
13+
## Explanation
14+
15+
Real world example
16+
17+
> When dealing with a user system, we need some operations on the user table. We can use the table module pattern in this scenario. We can create a class named UserTableModule and initialize a instance of that class to handle the business logic for all rows in the user table.
18+
19+
In plain words
20+
21+
> A single instance that handles the business logic for all rows in a database table or view.
22+
23+
Programmatic Example
24+
25+
In the example of the user system, we need to deal with the domain logic of user login and user registration. We can use the table module pattern and create an instance of the class `UserTableModule` to handle the business logic for all rows in the user table.
26+
27+
Here is the basic `User` entity.
28+
29+
```java
30+
@Setter
31+
@Getter
32+
@ToString
33+
@EqualsAndHashCode
34+
@AllArgsConstructor
35+
public class User {
36+
private int id;
37+
private String username;
38+
private String password;
39+
}
40+
```
41+
42+
Here is the `UserTableModule` class.
43+
44+
```java
45+
public class UserTableModule {
46+
private final DataSource dataSource;
47+
private Connection connection = null;
48+
private ResultSet resultSet = null;
49+
private PreparedStatement preparedStatement = null;
50+
51+
public UserTableModule(final DataSource userDataSource) {
52+
this.dataSource = userDataSource;
53+
}
54+
55+
/**
56+
* Login using username and password.
57+
*
58+
* @param username the username of a user
59+
* @param password the password of a user
60+
* @return the execution result of the method
61+
* @throws SQLException if any error
62+
*/
63+
public int login(final String username, final String password) throws SQLException {
64+
// Method implementation.
65+
66+
}
67+
68+
/**
69+
* Register a new user.
70+
*
71+
* @param user a user instance
72+
* @return the execution result of the method
73+
* @throws SQLException if any error
74+
*/
75+
public int registerUser(final User user) throws SQLException {
76+
// Method implementation.
77+
}
78+
}
79+
```
80+
81+
In the class `App`, we use an instance of the `UserTableModule` to handle user login and registration.
82+
83+
```java
84+
// Create data source and create the user table.
85+
final var dataSource = createDataSource();
86+
createSchema(dataSource);
87+
userTableModule = new UserTableModule(dataSource);
88+
89+
//Initialize two users.
90+
var user1 = new User(1, "123456", "123456");
91+
var user2 = new User(2, "test", "password");
92+
93+
//Login and register using the instance of userTableModule.
94+
userTableModule.registerUser(user1);
95+
userTableModule.login(user1.getUsername(), user1.getPassword());
96+
userTableModule.login(user2.getUsername(), user2.getPassword());
97+
userTableModule.registerUser(user2);
98+
userTableModule.login(user2.getUsername(), user2.getPassword());
99+
100+
deleteSchema(dataSource);
101+
```
102+
103+
The program output:
104+
105+
```java
106+
12:22:13.095 [main] INFO com.iluwatar.tablemodule.UserTableModule - Register successfully!
107+
12:22:13.117 [main] INFO com.iluwatar.tablemodule.UserTableModule - Login successfully!
108+
12:22:13.128 [main] INFO com.iluwatar.tablemodule.UserTableModule - Fail to login!
109+
12:22:13.136 [main] INFO com.iluwatar.tablemodule.UserTableModule - Register successfully!
110+
12:22:13.144 [main] INFO com.iluwatar.tablemodule.UserTableModule - Login successfully!
111+
```
112+
113+
## Class diagram
114+
115+
![](./etc/table-module.urm.png "table module")
116+
117+
## Applicability
118+
119+
Use the Table Module Pattern when
120+
121+
- Domain logic is simple and data is in tabular form.
122+
- The application only uses a few shared common table-oriented data structures.
123+
124+
## Related patterns
125+
126+
- [Transaction Script](https://java-design-patterns.com/patterns/transaction-script/)
127+
128+
- Domain Model
129+
130+
## Credits
131+
132+
* [Table Module Pattern](http://wiki3.cosc.canterbury.ac.nz/index.php/Table_module_pattern)
133+
* [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321127420&linkId=18acc13ba60d66690009505577c45c04)
134+
* [Architecture patterns: domain model and friends](https://inviqa.com/blog/architecture-patterns-domain-model-and-friends)

table-module/etc/table-module.urm.png

75.5 KB
Loading
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
@startuml
2+
package com.iluwatar.tablemodule {
3+
class App {
4+
- DB_URL : String {static}
5+
- LOGGER : Logger {static}
6+
- App()
7+
- createDataSource() : DataSource {static}
8+
- createSchema(dataSource : DataSource) {static}
9+
- deleteSchema(dataSource : DataSource) {static}
10+
+ main(args : String[]) {static}
11+
}
12+
class User {
13+
- id : int
14+
- password : String
15+
- username : String
16+
+ User(id : int, username : String, password : String)
17+
# canEqual(other : Object) : boolean
18+
+ equals(o : Object) : boolean
19+
+ getId() : int
20+
+ getPassword() : String
21+
+ getUsername() : String
22+
+ hashCode() : int
23+
+ setId(id : int)
24+
+ setPassword(password : String)
25+
+ setUsername(username : String)
26+
+ toString() : String
27+
}
28+
class UserTableModule {
29+
+ CREATE_SCHEMA_SQL : String {static}
30+
+ DELETE_SCHEMA_SQL : String {static}
31+
- LOGGER : Logger {static}
32+
- dataSource : DataSource
33+
+ UserTableModule(userDataSource : DataSource)
34+
+ login(username : String, password : String) : int
35+
+ registerUser(user : User) : int
36+
}
37+
}
38+
@enduml

table-module/pom.xml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
The MIT License
5+
Copyright © 2014-2021 Ilkka Seppälä
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
25+
-->
26+
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
27+
xmlns="http://maven.apache.org/POM/4.0.0"
28+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
29+
<parent>
30+
<artifactId>java-design-patterns</artifactId>
31+
<groupId>com.iluwatar</groupId>
32+
<version>1.25.0-SNAPSHOT</version>
33+
</parent>
34+
<modelVersion>4.0.0</modelVersion>
35+
36+
<artifactId>table-module</artifactId>
37+
38+
<dependencies>
39+
<dependency>
40+
<groupId>com.h2database</groupId>
41+
<artifactId>h2</artifactId>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.junit.jupiter</groupId>
45+
<artifactId>junit-jupiter-engine</artifactId>
46+
<scope>test</scope>
47+
</dependency>
48+
<dependency>
49+
<groupId>org.mockito</groupId>
50+
<artifactId>mockito-core</artifactId>
51+
</dependency>
52+
</dependencies>
53+
<build>
54+
<plugins>
55+
<plugin>
56+
<groupId>org.apache.maven.plugins</groupId>
57+
<artifactId>maven-assembly-plugin</artifactId>
58+
<executions>
59+
<execution>
60+
<configuration>
61+
<archive>
62+
<manifest>
63+
<mainClass>com.iluwatar.tablemodule.App</mainClass>
64+
</manifest>
65+
</archive>
66+
</configuration>
67+
</execution>
68+
</executions>
69+
</plugin>
70+
</plugins>
71+
</build>
72+
73+
</project>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.iluwatar.tablemodule;
2+
3+
import java.sql.SQLException;
4+
import javax.sql.DataSource;
5+
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.h2.jdbcx.JdbcDataSource;
8+
9+
10+
/**
11+
* Table Module pattern is a domain logic pattern.
12+
* In Table Module a single class encapsulates all the domain logic for all
13+
* records stored in a table or view. It's important to note that there is no
14+
* translation of data between objects and rows, as it happens in Domain Model,
15+
* hence implementation is relatively simple when compared to the Domain
16+
* Model pattern.
17+
*
18+
* <p>In this example we will use the Table Module pattern to implement register
19+
* and login methods for the records stored in the user table. The main
20+
* method will initialise an instance of {@link UserTableModule} and use it to
21+
* handle the domain logic for the user table.</p>
22+
*/
23+
@Slf4j
24+
public final class App {
25+
private static final String DB_URL = "jdbc:h2:~/test";
26+
27+
/**
28+
* Private constructor.
29+
*/
30+
private App() {
31+
32+
}
33+
34+
/**
35+
* Program entry point.
36+
*
37+
* @param args command line args.
38+
* @throws SQLException if any error occurs.
39+
*/
40+
public static void main(final String[] args) throws SQLException {
41+
// Create data source and create the user table.
42+
final var dataSource = createDataSource();
43+
createSchema(dataSource);
44+
var userTableModule = new UserTableModule(dataSource);
45+
46+
// Initialize two users.
47+
var user1 = new User(1, "123456", "123456");
48+
var user2 = new User(2, "test", "password");
49+
50+
// Login and register using the instance of userTableModule.
51+
userTableModule.registerUser(user1);
52+
userTableModule.login(user1.getUsername(), user1.getPassword());
53+
userTableModule.login(user2.getUsername(), user2.getPassword());
54+
userTableModule.registerUser(user2);
55+
userTableModule.login(user2.getUsername(), user2.getPassword());
56+
57+
deleteSchema(dataSource);
58+
}
59+
60+
private static void deleteSchema(final DataSource dataSource)
61+
throws SQLException {
62+
try (var connection = dataSource.getConnection();
63+
var statement = connection.createStatement()) {
64+
statement.execute(UserTableModule.DELETE_SCHEMA_SQL);
65+
}
66+
}
67+
68+
private static void createSchema(final DataSource dataSource)
69+
throws SQLException {
70+
try (var connection = dataSource.getConnection();
71+
var statement = connection.createStatement()) {
72+
statement.execute(UserTableModule.CREATE_SCHEMA_SQL);
73+
}
74+
}
75+
76+
private static DataSource createDataSource() {
77+
var dataSource = new JdbcDataSource();
78+
dataSource.setURL(DB_URL);
79+
return dataSource;
80+
}
81+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.iluwatar.tablemodule;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
import lombok.Setter;
7+
import lombok.ToString;
8+
9+
10+
/**
11+
* A user POJO that represents the data that will be read from the data source.
12+
*/
13+
@Setter
14+
@Getter
15+
@ToString
16+
@EqualsAndHashCode
17+
@AllArgsConstructor
18+
public class User {
19+
private int id;
20+
private String username;
21+
private String password;
22+
23+
}

0 commit comments

Comments
 (0)