ドメイン駆動開発道

ドメイン駆動開発を実践できず、悶々とする毎日を送るおやじSEのブログです。

ドメインモデルの実装(4) ~PlaceOrderServiceTest~

前回モック用に作ったインターフェースを利用して、PlaceOrderServiceのテストコードを書きます。

@RunWith(JMock.class)
public class PlaceOrderServiceTests  extends TestCase  {

	private PendingOrderRepository pendingOrderRepository;
	private PendingOrder pendingOrder;
	
	private String pendingOredrId;
	private Date goodDeliveryTime;
	private Address goodDeliveryAddress;
	private PlaceOrderService service;
	
	private Mockery context = new JUnit4Mockery();
	
	
	public void setUp() throws Exception {
		pendingOrderRepository = context.mock(PendingOrderRepository.class);
		service = new PlaceOrderServiceImpl(pendingOrderRepository);
		
		pendingOrder = context.mock(PendingOrder.class);
		
		goodDeliveryAddress = new Address();
		goodDeliveryTime = new Date();
		
		pendingOredrId = "pendingOrderId";
		
	}
	
	@Test
	public void testUpdateDeliveryInfo_Good() {
		
		context.checking(new Expectations() {{
		     allowing(pendingOrderRepository).findOrCreatePendingOrder(pendingOredrId);
		     will(returnValue(pendingOrder));
		}});
		
		
		context.checking(new Expectations() {{
		      allowing(pendingOrder).updateDeliveryInfo(goodDeliveryAddress,goodDeliveryTime);
		      will(returnValue(true));
		}});
		
		PlaceOrderServiceResult result = service.updateDeliveryInfo(pendingOredrId, goodDeliveryAddress, goodDeliveryTime);
		
		PendingOrder pendingOrder = result.getPendingOrder();
		
		assertTrue(result.isSuccess());
		
		PendingOrder returnPendingOrder = result.getPendingOrder();
		assertSame(pendingOrder, returnPendingOrder);
		
		assertEquals(goodDeliveryAddress, pendingOrder.getDeliveryAddress());
		assertEquals(goodDeliveryTime, pendingOrder.getDeliveryTime());
		
	}
	

}

ドメインモデルの実装(3) ~implementing the method~

テストケースに対するアプリ側のコードを実装します。

public class PlaceOrderServiceImpl implements PlaceOrderService {
	
	private PendingOrderRepository pendingOrderRepository;
	
	public PlaceOrderService(PendingOederRepository repository) {
		this.pendingOrderRepository = repository;
	}
	
	public PlaceOrderServiceResult updateDeliveryInfo(
			String pendingOrderId, Addres deliveryAddress, Date deliveryTime) {
		PendingOeder pendingOrder = pendingOrderRepository.findOrCreatePendingOrder(pendingOederId);
		
		boolean success = pendingOrder.updateDeliveryInfo(deliveryAddress, deliveryTime);
		
		return new PlaceOedreServiceResult(success, pendingOrder)
	}

}

PlaceOrderServiceは、PendingOrderRepositoryとPendingOrderClassとコラボレートします。
しかしこの時点では、まだ深入りしたくないので、モックを使用します。

jMockを使用するにあたり、PendingOrderRepositoryとPendingOrderの各インターフェース必要とします。

PendingOrderRepositoryは、findOrderCreatePendingOrder()を定義します。

public interface PendingOrderRepository {
	
	PendingOrder findOrCreatePendingOrder(String pendingOrderId);

}

PendingOrderのupdateDeliveryInfo()は、とりあえずfalseを返します。

public interface PendingOrder {
	
	public boolean updateDeliveryInfo(Address deliveryAddress, Date deliveryTime) ;

}

ドメインモデルの実装(2) ~The valid delivery Information test case~

updateDeliveryInfo()が呼ばれるとき、PlaceOrderServiceはPendingOrderを読み出すか、存在しなければ生成する。
まずは、大枠として以下のシナリオを想定する。

1) 入力されたデリバリ時刻が未来日時で、デリバリ情報に該当する少なくとも1つのレストランがある場合、PalceOrderServiceはPendingOrderを新しいデリバリ情報で更新する。
そして、成功ステータスコードとPendingOrderを含むPlaceOrderServiceResultを返す。

2) 入力されたデリバリ時刻が未来日時ではなく、または、デリバリ情報に該当する少なくとも1つのレストランがない場合、PalceOrderServiceはPendingOrderを更新しない。
そして、失敗ステータスコードとPendingOrderを含むPlaceOrderServiceResultを返す。

まずは、1)のシナリオのテストケースを記述するとこんな感じ。
# makeGoodDeliveryAddress()、makeGoodDeliveryTime()は別定義。

public class PlaceOrderServiceTests {

	private PlaceOrderService service;
	
	public void setUp() throws Exception {
		service = new PalaceOrderServiceImpl();
	}
	
	@Test
	public void testUpdateDeliveryInfo_Valid() {
		
		Address  deliveryAddress = makeGoodDeliveryAddress();
		Date deliveryTime = makeGoodDeliveryTime();
		
		String pendingOrderId = null;
		
		PlaceOrderServiceResult result = service.updateDeliveryInfo(pendingOrderId, deliveryAddress, deliveryTime);
		
		PendingOrder pendingOrder = result.getPendingOrder();
		
		assertTrue(result.isSuccess());
		assertEquals(deliveryAddress, pendingOrder.getDeliveryAddress());
		assertEquals(deliveryTime, pendingOrder.getDeliveryTime());
		
	}

}

ドメインモデルの実装(1)

最初にメソッドupdateDeliveryInfo()を実装します。
その後、updateDeliveryInfo()の中で呼ばれるPendingOrderのメソッドを実装します。
また、必要とされるリポジトリも識別し実装します。

まずは、サービスのメソッドを実装。

public interface PlaceOrderService {
  PlaceOrderServiceResult updateDeliveryInfo(String pendingOrderId, 
                                             Address deliveryAddress, 
                                             Date deliveryTime);
}

引数はPendingOrderId、deliveryAddress、deliveryTimeの3つ。
PendingOrderIdはDB上のPendingOrderの主キー。HttpSessionやブラウザのプレゼン層で保持される
deliveryAddressとdeliveryTimeは、ユーザによって入力されるデリバリ情報を示す。

開発環境

これから実装を行っていくにあたり、環境を記しておきます。
使用するライブラリは順次追加していくつもりです。

JDK: Java SE 6 update31
Tomcat:Tomcat 6.0.35
開発ツール:Spring Tool Suite(STS) 3.0.0

Maven Dependenciesライブラリ:

No. GroupID Artifact ID Version Scope 説明
1 org.springframework spring-context 3.1.1.RELEASE compile Spring Context
2 org.springframework spring-webmvc 3.1.1.RELEASE compile Spring MVC
3

システムシーケンス図の作成

前回、システムイベントメッセージを抽出しましたので、それをもとにシステムシーケンス図を作成します。
システムシーケンス図については、書籍「実践UML」が参考になります。
とりあえず、最初のメッセージにだけ、引数を入れてみます。

f:id:katzn:20130508235416p:plain


[参考]
実践UML 第3版 オブジェクト指向分析設計と反復型開発入門

システムイベントメッセージの抽出

ユースケースから、お客のリクエスト(システムイベントメッセージ)を抽出します。そのシステムイベントメッセージ毎にメソッド名をつけます。

No. システムイベントメッセージ 説明 メソッド
1 Enter delivery Info お客がデリバリー情報を入力する。 UpadateDeriveryInfo
2 Select restaurant お客がレストランを選択する。 updateRestaurant
3 Update quantities お客がメニューとその数を入力する。 updateQuantities
4 Check out お客が入力したメニューと数が正しいことを確認する。 checkout
5 Entry payment information お客が支払い(クレジットカード)情報 を入力する。 updatePaymentInformation
6 Place Order お客が注文した内容を確認する。 placeOrder

次回、これをもとにシステムシーケンス図を作成します。