clean Architecture with springboot
參考來源
來源:https://code-specialist.com/architecture/rest-api-clean-architecture
分層命名
entites layer (命名為 domain)
use cases (命名為 application)
interface adapter (adapter)
framwork & db (plugin)
架構如何分層
- Monolithic - divided by packages
- Usage of modules - e.g. make use of maven (recommended)
Domain Layer 物件概念
- CommonProductFactory
- ICommonProuductFactory
- CommonProduct
- ICommonProduct
- SpcProductFactory
- SpcProduct
- ISpcProduct
test domain
@Test
public void test_givenAbcName_whenNameIsNotValid_thenIsFalse(){
IProduct product = new CommonProduct("Abc", "Name", "description", 0.5d);
assertFalse(product.nameIsValid());
}
Applicaiton Layer 物件概念
- CommonProductInteractor (use case 邏輯)
- CommonProductRegisterInputBoundary (供外部參考的interface)
- CommonProductRegisterInputRequestModel (供外部參考的request)
- CommonProductRegisterOutputRequestModel (供外部參考的request)
- IPresenter (需要依賴的外層物件,用interface達到依賴反轉)
- CommonProductRegisterDsGateWay (需要依賴的持久化層物件,用interface達到依賴反轉)
- CommonProductRegisterAPIGateWay (需要依賴的外部API物件,用interface達到依賴反轉)
test applicaition 層
mock
test logicpublic class CommonProductInteractorTest { ICommonProductFactory mockedFactory; ICommonProductPresenter mockedPresenter; ICommonProductRegisterGateway mockedGateway; @BeforeEach void setup(){ mockedGateway = Mockito.mock(ICommonProductRegisterGateway.class); mockedFactory = Mockito.mock(ICommonProductFactory.class); mockedPresenter = Mockito.mock(ICommonProductPresenter.class); } }@Test void givenValidCommonProductProperties_whenCreate_thenSaveItAndPrepareSuccessView() throws ProductCustomException { // ARRANGE CommonProductRequestModel requestModel = new CommonProductRequestModel("TestId", "ValidTestName", "Test description", 52.2); long timestmap = 1668617824L; CommonProduct product = new CommonProduct("001", "ValidName", "Some Description", 25.25d, timestmap); CommonProductResponseModel responseModel = new CommonProductResponseModel(product.getId(), product.getName(), product.getDescription(), product.getPrice(), String.valueOf(product.getCreatedAt())); CommonProductResponseModel finalResponseModel = new CommonProductResponseModel(product.getId(), product.getName(), product.getDescription(), product.getPrice(), "2022-11-16"); Mockito.when(mockedGateway.existsById(requestModel.getId())).thenReturn(false); Mockito.when(mockedFactory.create(requestModel.getId(), requestModel.getName(), requestModel.getDescription(), requestModel.getPrice())).thenReturn(product); Mockito.when(mockedPresenter.prepareSuccessView(responseModel)).thenReturn(finalResponseModel); CommonProductInteractor interactor = new CommonProductInteractor(mockedPresenter, mockedFactory, mockedGateway); // ACT CommonProductResponseModel verifyResponseModel = interactor.create(requestModel); // ASSERT Assertions.assertEquals("2022-11-16", finalResponseModel.getCreatedAt()); Mockito.verify(mockedGateway, Mockito.times(1)).save(product); Mockito.verify(mockedGateway, Mockito.times(1)).existsById(requestModel.getId()); Mockito.verify(mockedPresenter, Mockito.times(1)).prepareSuccessView(responseModel); Assertions.assertEquals(finalResponseModel.getId(), verifyResponseModel.getId()); Assertions.assertEquals(finalResponseModel.getName(), verifyResponseModel.getName()); Assertions.assertEquals(finalResponseModel.getDescription(), verifyResponseModel.getDescription()); Assertions.assertEquals(finalResponseModel.getPrice(), verifyResponseModel.getPrice()); Assertions.assertEquals("2022-11-16", verifyResponseModel.getCreatedAt()); }
Adapter Layer 物件概念
- CommonProductRegisterDsGateWayImpl (作為框架持久層的adapter實現gateWay)
- CommonProductRegisterAPIGateWayImpl (作為API的adapter實現gateWay)
Plugin Layer 物件概念
- springboot Application (psvm)
- BeanConfig (依賴注入使用)
- restcontroller
- service