Javaにおけるトランザクションスクリプトパターン:統合されたスクリプトによるビジネスロジックの簡素化
別名
- スクリプト化されたトランザクション
トランザクションスクリプトデザインパターンの目的
Javaにおけるトランザクションスクリプトパターンは、各プロシージャがプレゼンテーションからの単一の要求を処理するプロシージャによってビジネスロジックを整理します。
実例を用いたトランザクションスクリプトパターンの詳細な説明
実例
トランザクションスクリプトデザインパターンの現実世界の類似例としては、銀行の窓口係の日常業務が挙げられます。預金、現金引き出し、口座間の送金など、各トランザクションが特定のスクリプトのようなプロシージャによって処理される銀行を想像してみてください。各プロシージャは必要な詳細(例:口座番号、金額)を受け取り、簡潔で段階的な方法でトランザクションを実行します。これにより、複雑なルールや相互作用のシステムを必要とせずに、各トランザクションが正しく独立して処理されます。このシンプルで整理されたアプローチにより、銀行の窓口係は一日のさまざまなトランザクションを効率的に管理できます。
簡単に言うと
トランザクションスクリプトは、システムが実行する必要があるトランザクションにビジネスロジックを整理します。
Wikipediaによると
トランザクションスクリプトデザインパターンは、アプリケーションのビジネスロジックを整理するための簡単な方法であり、プレゼンテーションレイヤーからの各要求を単一のプロシージャで処理できるシナリオに特に適しています。このパターンは、単純なアプリケーションや、迅速な開発と容易な理解が重要なシステムでよく使用されます。各トランザクションスクリプトは、注文の処理や結果の計算など、特定のタスクを担当し、通常はデータベースと直接やり取りします。
Javaにおけるトランザクションスクリプトパターンのプログラミング例
私たちのJavaにおけるトランザクションスクリプトパターンの例は、ホテルの部屋の予約に関するものです。
Hotel
クラスは、部屋の予約とキャンセルを処理します。
@Slf4j
public class Hotel {
private final HotelDaoImpl hotelDao;
public Hotel(HotelDaoImpl hotelDao) {
this.hotelDao = hotelDao;
}
public void bookRoom(int roomNumber) throws Exception {
Optional<Room> room = hotelDao.getById(roomNumber);
if (room.isEmpty()) {
throw new Exception("Room number: " + roomNumber + " does not exist");
} else {
if (room.get().isBooked()) {
throw new Exception("Room already booked!");
} else {
Room updateRoomBooking = room.get();
updateRoomBooking.setBooked(true);
hotelDao.update(updateRoomBooking);
}
}
}
public void cancelRoomBooking(int roomNumber) throws Exception {
Optional<Room> room = hotelDao.getById(roomNumber);
if (room.isEmpty()) {
throw new Exception("Room number: " + roomNumber + " does not exist");
} else {
if (room.get().isBooked()) {
Room updateRoomBooking = room.get();
updateRoomBooking.setBooked(false);
int refundAmount = updateRoomBooking.getPrice();
hotelDao.update(updateRoomBooking);
LOGGER.info("Booking cancelled for room number: " + roomNumber);
LOGGER.info(refundAmount + " is refunded");
} else {
throw new Exception("No booking for the room exists");
}
}
}
}
Hotel
クラスには、それぞれ部屋の予約とキャンセルを行う2つのメソッドがあります。それぞれがシステム内の単一のトランザクションを処理するため、Hotel
はトランザクションスクリプトパターンを実装しています。
bookRoom
メソッドは、部屋が既に予約されているかどうかをチェックする、予約されていない場合は部屋を予約し、DAOを使用してデータベースを更新するなど、必要なすべてのステップを統合します。
cancelRoom
メソッドは、部屋が予約されているかどうかをチェックする、予約されている場合は返金額を計算し、DAOを使用してデータベースを更新するなどのステップを統合します。
例を実行するためのmain
メソッドを含むApp
クラスを次に示します。
public class App {
private static final String H2_DB_URL = "jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1";
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
public static void main(String[] args) throws Exception {
final var dataSource = createDataSource();
deleteSchema(dataSource);
createSchema(dataSource);
final var dao = new HotelDaoImpl(dataSource);
// Add rooms
addRooms(dao);
// Print room booking status
getRoomStatus(dao);
var hotel = new Hotel(dao);
// Book rooms
hotel.bookRoom(1);
hotel.bookRoom(2);
hotel.bookRoom(3);
hotel.bookRoom(4);
hotel.bookRoom(5);
hotel.bookRoom(6);
// Cancel booking for a few rooms
hotel.cancelRoomBooking(1);
hotel.cancelRoomBooking(3);
hotel.cancelRoomBooking(5);
getRoomStatus(dao);
deleteSchema(dataSource);
}
private static void getRoomStatus(HotelDaoImpl dao) throws Exception {
try (var customerStream = dao.getAll()) {
customerStream.forEach((customer) -> LOGGER.info(customer.toString()));
}
}
private static void deleteSchema(DataSource dataSource) throws java.sql.SQLException {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(RoomSchemaSql.DELETE_SCHEMA_SQL);
}
}
private static void createSchema(DataSource dataSource) throws Exception {
try (var connection = dataSource.getConnection();
var statement = connection.createStatement()) {
statement.execute(RoomSchemaSql.CREATE_SCHEMA_SQL);
} catch (Exception e) {
throw new Exception(e.getMessage(), e);
}
}
private static DataSource createDataSource() {
var dataSource = new JdbcDataSource();
dataSource.setUrl(H2_DB_URL);
return dataSource;
}
private static void addRooms(HotelDaoImpl hotelDao) throws Exception {
for (var room : generateSampleRooms()) {
hotelDao.add(room);
}
}
private static List<Room> generateSampleRooms() {
final var room1 = new Room(1, "Single", 50, false);
final var room2 = new Room(2, "Double", 80, false);
final var room3 = new Room(3, "Queen", 120, false);
final var room4 = new Room(4, "King", 150, false);
final var room5 = new Room(5, "Single", 50, false);
final var room6 = new Room(6, "Double", 80, false);
return List.of(room1, room2, room3, room4, room5, room6);
}
}
App.java
ファイルには、アプリケーションのmain
エントリポイントがあります。これは、ホテル管理のコンテキストでトランザクションスクリプトパターンの使用を示しています。次に、何が起こるかを段階的に説明します。
createDataSource()
メソッドを使用して、新しいH2データベースデータソースが作成されます。このデータソースは、インメモリH2データベースとのやり取りに使用されます。deleteSchema(dataSource)
メソッドを使用して、データベース内の既存のスキーマ(存在する場合)が削除されます。これは、アプリケーションの起動前にクリーンな状態を確保するために行われます。createSchema(dataSource)
メソッドを使用して、データベースに新しいスキーマが作成されます。スキーマには、アプリケーションに必要なテーブルとリレーションが含まれています。データアクセスオブジェクト(DAO)として機能する
HotelDaoImpl
のインスタンスが作成されます。このオブジェクトは、ホテルの部屋に関するデータベース操作の処理を担当します。addRooms(dao)
メソッドを使用して、サンプルの部屋がホテルに追加されます。このメソッドは、サンプルの部屋のリストを生成し、DAOを介してホテルに追加します。getRoomStatus(dao)
メソッドを使用して、すべての部屋の予約状況が出力されます。DAOをパラメーターとして
Hotel
のインスタンスが作成されます。このオブジェクトはホテルを表し、部屋の予約とキャンセルを行うメソッドを提供します。hotel.bookRoom(roomNumber)
メソッドを使用して、いくつかの部屋が予約されます。次に、
hotel.cancelRoomBooking(roomNumber)
メソッドを使用して、一部の予約がキャンセルされます。変更を反映するために、すべての部屋の予約状況が再び出力されます。
最後に、
deleteSchema(dataSource)
メソッドを使用して、データベース内のスキーマが再び削除され、データベースの状態がクリーンアップされます。
コンソール出力
14:22:20.050 [main] INFO com.iluwatar.transactionscript.App -- Room(id=1, roomType=Single, price=50, booked=false)
14:22:20.051 [main] INFO com.iluwatar.transactionscript.App -- Room(id=2, roomType=Double, price=80, booked=false)
14:22:20.051 [main] INFO com.iluwatar.transactionscript.App -- Room(id=3, roomType=Queen, price=120, booked=false)
14:22:20.051 [main] INFO com.iluwatar.transactionscript.App -- Room(id=4, roomType=King, price=150, booked=false)
14:22:20.051 [main] INFO com.iluwatar.transactionscript.App -- Room(id=5, roomType=Single, price=50, booked=false)
14:22:20.051 [main] INFO com.iluwatar.transactionscript.App -- Room(id=6, roomType=Double, price=80, booked=false)
14:22:20.058 [main] INFO com.iluwatar.transactionscript.Hotel -- Booking cancelled for room number: 1
14:22:20.058 [main] INFO com.iluwatar.transactionscript.Hotel -- 50 is refunded
14:22:20.059 [main] INFO com.iluwatar.transactionscript.Hotel -- Booking cancelled for room number: 3
14:22:20.059 [main] INFO com.iluwatar.transactionscript.Hotel -- 120 is refunded
14:22:20.059 [main] INFO com.iluwatar.transactionscript.Hotel -- Booking cancelled for room number: 5
14:22:20.059 [main] INFO com.iluwatar.transactionscript.Hotel -- 50 is refunded
14:22:20.060 [main] INFO com.iluwatar.transactionscript.App -- Room(id=1, roomType=Single, price=50, booked=false)
14:22:20.060 [main] INFO com.iluwatar.transactionscript.App -- Room(id=2, roomType=Double, price=80, booked=true)
14:22:20.060 [main] INFO com.iluwatar.transactionscript.App -- Room(id=3, roomType=Queen, price=120, booked=false)
14:22:20.060 [main] INFO com.iluwatar.transactionscript.App -- Room(id=4, roomType=King, price=150, booked=true)
14:22:20.060 [main] INFO com.iluwatar.transactionscript.App -- Room(id=5, roomType=Single, price=50, booked=false)
14:22:20.060 [main] INFO com.iluwatar.transactionscript.App -- Room(id=6, roomType=Double, price=80, booked=true)
このパターンは、単純なビジネスロジックに適しており、容易に理解および保守できます。
Javaでトランザクションスクリプトパターンを使用する場面
- ビジネスロジックが単純で、個々のプロシージャに容易に整理できる場合に使用します。
- 単純なトランザクション要件を持つアプリケーション、またはドメインモデルのような複雑なアーキテクチャを正当化しないロジックを持つアプリケーションに適しています。
トランザクションスクリプトパターンのJavaチュートリアル
Javaにおけるトランザクションスクリプトパターンの実用例
- 迅速な開発が重要な初期段階のスタートアップや小規模なアプリケーション。
- 銀行取引や電子商取引の注文処理など、明確に定義されたプロシージャを持つエンタープライズアプリケーション。
- ビジネスロジックが既にスクリプトとして記述されているレガシーシステム。
トランザクションスクリプトパターンのメリットとデメリット
メリット
- トランザクションスクリプトパターンを利用することで、特にスタートアップ環境において、コードのシンプルさと開発サイクルの高速化が向上します。
- シンプルで実装が容易。
- 単純なビジネスロジックについては、理解と保守が容易。
- 小規模アプリケーションの開発サイクルが高速。
デメリット
- 注意深く管理しないと、コードの重複につながる可能性があります。
- 複雑なビジネスロジックには適していません。アプリケーションが成長するにつれて、管理不能になる可能性があります。
- ドメインモデルのようなより構造化されたアプローチと比較して、単体テストが困難です。
関連するJavaデザインパターン
- ドメインモデル:トランザクションスクリプトとは異なり、ドメインモデルはビジネスロジックをデータモデルを中心に構成し、複雑なビジネスルールに適しています。
- サービス層:アプリケーションの境界を定義し、ビジネスロジックをカプセル化するために、トランザクションスクリプトと共に使用されることがよくあります。
- テーブルモジュール:トランザクションスクリプトに似ていますが、要求ごとにプロシージャではなく、テーブルごとに1つのクラスを使用してロジックを整理します。