본문으로 건너뛰기
ElastiCORE 3.0 은 릴리스 후보(RC)입니다: Maven Central 의 3.0.261022-rc1. 2.2 라인 프로젝트는 2.2 문서를 보세요.
버전: 3.0

4단계: 백오피스, 이벤트, 지표, 메시지

백오피스​

관리자로 로그인하면 쇼핑몰 헤더에 Back office 가 보입니다: 최신 주문부터, 배송할 수 있는 주문에는 Ship 버튼. 화면은 생성된 클라이언트 위의 평범한 TypeScript 이고, 누가 어떤 상태에서 배송할 수 있는지는 서버가 정합니다:

web/src/main.ts (예제의 화면, 줄임)
async function admin(): Promise<void> {
if (customer?.role !== UserRole.ADMIN) return go('#/signin');
const page = await Orders.search({ sortColumn: 'orderDate', sortAscending: false, pageSize: 50 });
render(`<h1>Back office - orders</h1>${orderTable(page.content, true)}<p class="error" id="error"></p>`);
on('button.ship', 'click', async (el) => {
try {
await Orders.ship(el.dataset.id!);
flash('Shipped');
admin();
} catch (e) {
document.getElementById('error')!.textContent = problem(e);
}
});
}

버튼은 Orders.canShip(o) 일 때 보입니다 - 생명주기 canShip() 의 생성된 짝입니다. 같은 검색이 고객에게는 자기 주문만(@access(read=owner)), 관리자에게는 전부를 답합니다. 고객의 주문 화면은 Orders.canCancel(o) 일 때 Cancel this order 를 보여 줍니다.

curl -s -c jar -b jar -X POST https://petstore.elasticore.io/api/petstore/customer/signin \
-H 'Content-Type: application/json' -d '{"login":"xsko","password":"xsko"}'
curl -s -c jar -b jar -X POST https://petstore.elasticore.io/api/petstore/purchaseOrder/{orderId}/ship

상태별 주문 수와 매출 같은 관리자 요약은 직접 만드는 연산입니다: 직접 쓰는 로직 의 상점 레시피 report() (ACCESS.require(currentUser, "report"))를 보세요.

이벤트​

@event(Placed(total), Shipped, Cancelled) 는 PurchaseOrderPlacedEvent(total 을 실음), PurchaseOrderShippedEvent, PurchaseOrderCancelledEvent 를 만듭니다. ship 과 cancel 은 생명주기 이동으로 자기 이벤트를 내고, place() 가 Placed 를 냅니다. 주문이 저장될 때 발행되고, 내 @EventListener 가 받습니다 - 예를 들어 배송된 주문마다 로그 한 줄:

@EventListener
void shipped(PurchaseOrderShippedEvent e) {
log.info("order {} SHIPPED", e.getOrderId());
}

여기서 Shipped 는 내용을 싣지 않으므로 이벤트에는 주문 ID 와 occurredOn 만 있습니다. 합계도 남기려면 에이전트 키트의 상점 레시피처럼 Shipped(total) 로 선언합니다.

outbox: true 면 같은 이벤트가 트랜잭션 outbox 를 거쳐 브로커로 갑니다.

BI·AI 를 위한 업무 지표​

blueprint/petstore/model/metrics.yml
# The shop's measures - what the business means by revenue, an order, the average order.
# Exported to the Apache Ossie semantic model (blueprint/semantic_model.yml) as metrics,
# so a BI or AI tool asks for "revenue" and gets this definition, not its own guess.
metrics:
revenue: sum(PurchaseOrder.total) where status != CANCELLED @terms(sales, 매출액) -- 매출 (취소 제외, VAT 포함·할인 후 금액)
orderCount: count(PurchaseOrder) where status != CANCELLED @terms(orders, 주문 건수) -- 주문 수 (취소 제외)
averageOrder: revenue / orderCount @terms(AOV, 객단가) -- 주문 1건 평균 금액
shippedOrders: count(PurchaseOrder) where status = SHIPPED -- 배송된 주문 수
cancelledOrders: count(PurchaseOrder) where status = CANCELLED -- 취소된 주문 수
customerCount: count(Customer) where role = USER @terms(members, 회원 수) -- 고객 수 (관리자 제외)

rules = ['ossie-export'] 이면 빌드가 blueprint/semantic_model.yml 을 씁니다: 모든 테이블, 컬럼, 조인, enum 코드, 그리고 이 지표들을 SQL 로. MetricsTest 는 주문을 접수·배송·취소한 뒤 내보낸 지표마다 DB 에서 실행해 엔티티로 계산한 값과 비교하고, SemanticModelTest 는 내보낸 모든 컬럼을 DB 와 대조합니다. Apache Ossie 참고.

한국어 오류 메시지​

빌드는 도메인이 답할 수 있는 모든 코드를 src/main/resources/i18n/petstore-messages.properties 에 쓰고, 예제는 필요한 것만 그 옆 petstore-messages_ko.properties 에 번역합니다:

error.state.title=지금 상태에서는 할 수 없는 요청
state.PurchaseOrder.status.cancel=주문 상태가 {0}이면 취소할 수 없습니다 (가능한 상태: {1})
invariant.PurchaseOrder.lines.minsize=장바구니가 비어 있습니다
Size.lines=장바구니가 비어 있습니다
typeMismatch.lines.quantity=수량은 0 이상의 정수입니다
POST /api/petstore/purchaseOrder/{id}/cancel
Accept-Language: ko

HTTP/1.1 409
{ "title": "지금 상태에서는 할 수 없는 요청",
"detail": "주문 상태가 SHIPPED이면 취소할 수 없습니다 (가능한 상태: [PLACED])",
"code": "state.PurchaseOrder.status.cancel", "status": 409 }

Accept-Language 가 없거나 번역이 없는 언어면 영어로 답합니다 - 서버 머신의 언어는 쓰지 않습니다. 메시지와 언어 참고.

화면​

web/src(main.ts, shop.ts, cart.ts)는 webBuild 태스크가 static/app.js 로 묶고 src/main/ts 에 대해 타입 검사를 합니다. 생성된 OrderStatusLabel, validateAddress, ApiError.fieldErrors, canShip / canCancel, CustomerApi.signIn / signOut / me 를 쓰고, 가입만 손으로 쓴 엔드포인트입니다:

web/src/shop.ts
export { signIn, signOut, me } from '../../src/main/ts/CustomerApi';

export const signUp = (customer: CustomerDTO) =>
request<CustomerDTO>('POST', '/api/petstore/account/signup', customer);

다음: 생성된 프로젝트 키워 가기 - 업무가 더 많은 것을 요구할 때.