Copyable Template
행위자가 명시적으로 테넌트 소유로 복사하는 선택적 시작 콘텐츠를 제공하고, 복사본이 원본을 추적하지 않는 이유를 이해합니다.
운영자가 선택해서 복사하는 시작 데이터를 제공합니다. 적용 후 레코드는 테넌트가 직접 관리합니다.
템플릿 선언과 적용
SDK CopyableTemplateContribution을 구현합니다. templates()는 버전이 있는 원본을 반환하고 apply()는 App 레코드를 저장한 뒤 공개 참조를 TemplateApplicationResult로 돌려줍니다. 키와 원본 형식, 테넌트 또는 Legal Entity 범위를 검사하고, 재시도할 때 기존 수정값을 보존하세요. 현재 구현은 packages/talent/src/Contribution/TalentDataTemplates.php에서 볼 수 있습니다.
Host 적용 명령에는 테넌트와 사용자를 명시합니다. 정확한 명령과 범위는 설치 콘텐츠에 있습니다. 업무에 반드시 필요한 행이라면 필수 설치 콘텐츠의 migration 경로를 사용하세요.
TEMPLATE_TENANT_ID에 대상 테넌트 ID, TEMPLATE_ACTOR_ID에 그 테넌트의 권한 있는 사용자 ID를 지정한 뒤 적용합니다.
task artisan -- nexia-apps:apply-template payroll smb_basic_elements \
--tenant="$TEMPLATE_TENANT_ID" --actor="$TEMPLATE_ACTOR_ID"결과 확인
적용 이력에 원본·버전과 생성 참조가 남고, 사용자가 복사본을 수정할 수 있어야 합니다. 재적용으로 중복이 생기거나 새 원본 버전 배포만으로 기존 복사본이 덮어써져서는 안 됩니다. App 테스트하기에서 이 사례를 검사합니다.
적용과 출처 이력
copyable template은 테넌트가 복사하기를 선택할 수 있는 콘텐츠입니다. 적용하면 편집 가능한 테넌트 소유 행이 생기고 출처가 기록됩니다.
packages/payroll/src/Contribution/PayrollDataTemplates.php:
<?php
declare(strict_types=1);
namespace Amuzcorp\Nexia\Payroll\Contribution;
use Amuzcorp\Nexia\Payroll\Domain\PayrollElementCatalog;
use Amuzcorp\Nexia\Payroll\Models\PayrollElement;
use Nexia\Templates\Contracts\CopyableTemplateContribution;
use Nexia\Templates\CopyableTemplate;
use Nexia\Templates\TemplateApplicationContext;
use Nexia\Templates\TemplateApplicationResult;
/** Tenant-copyable SMB baseline; applying it creates editable Payroll-owned records. */
final class PayrollDataTemplates implements CopyableTemplateContribution
{
private const KEY = 'smb_basic_elements';
public function templates(): array
{
return [new CopyableTemplate('payroll', self::KEY, '2', CopyableTemplate::SCOPE_TENANT, PayrollElementCatalog::definitions())];
}
public function apply(CopyableTemplate $template, TemplateApplicationContext $context): TemplateApplicationResult
{
if ($template->key !== self::KEY || ! is_array($template->source)) {
throw new \InvalidArgumentException('Unsupported Payroll data template.');
}
$refs = [];
foreach ($template->source as $definition) {
$element = PayrollElement::query()->firstOrCreate(
['code' => $definition['code']],
$definition,
);
$refs[] = ['type' => 'payroll.payroll_element', 'id' => (string) $element->public_id, 'key' => $element->code, 'version' => 1];
}
return new TemplateApplicationResult($refs);
}
}인스턴스 메서드 둘입니다. templates()가 복사 가능한 것을 선언하고, apply()가 복사를 수행하며 만든 행의 참조를 돌려줍니다.
Contribution 발견
contribution 지점은 Nexia\Templates\Contracts\CopyableTemplateContribution으로 공개되어 있습니다. 새 App은 이것을 직접 구현할 수 있고, 호스트는 App이 선언한 contribution 위치에서 클래스를 발견합니다.
SDK는 CopyableTemplate, TemplateApplicationContext, TemplateApplicationResult도 공개하므로 선언과 적용 경계 전체가 Nexia\* 아래에 머뭅니다. 구현 사례는 packages/payroll과 packages/talent에서 확인할 수 있습니다.
값 객체
CopyableTemplate은 비어 있지 않은 app, key, version과 상수 둘 중 하나인 범위를 요구합니다.
| 상수 | 값 |
|---|---|
CopyableTemplate::SCOPE_TENANT | tenant |
CopyableTemplate::SCOPE_LEGAL_ENTITY | legal_entity |
다른 값에는 예외를 던집니다.
A copyable template requires an app, key, and version.
Unsupported copyable template scope [organization].
올바른 apply()의 세 성질
| 성질 | 방법 | 이유 |
|---|---|---|
| 멱등 | 업무 식별자를 키로 firstOrCreate | 두 번 적용해도 중복되지 않아야 함 |
| 참조를 돌려줌 | public_id를 담은 ['type', 'id', 'key', 'version'] | 출처 기록. 숫자 키는 절대 아님 |
| 입력을 검증 | 소유하지 않은 키를 거부 | 런타임이 걸러줬다고 가정하지 않음 |
출처 원장
템플릿을 적용하면 테넌트 로컬 template_applications 원장에 항목이 기록됩니다.
| 원장 필드 | 내용 |
|---|---|
| App 키 | 소유 App |
| 템플릿 키와 버전 | 불변 원본 식별자 |
| 범위 | tenant 또는 legal_entity |
| Legal Entity | nullable |
| 행위자 | 누가 적용했는가 |
| 결과 상태 | 성공 또는 실패 |
| 실패 이유 | 실패 시 |
| 생성된 resource 참조 | 복사가 만든 것 |
원장은 출처 기록만입니다. 템플릿 원본 카탈로그가 되지 않고, 복사본의 이후 편집을 추적하지도 않습니다.
원본과 복사본의 변경
복사본은 만들어지는 순간 갈라집니다. 템플릿을 고쳐서 수정을 배포하고 기존 테넌트가 받기를 기대하지 마세요.
| 하는 일 | 기존 복사본에 미치는 영향 |
|---|---|
| 템플릿 원본 편집 | 없음 |
version 올림 | 없음 — 이후 적용만 달라짐 |
| 템플릿 폐기 | 없음 — 복사본은 테넌트 것 |
기존 테넌트에 새 콘텐츠를 전하려면 누군가 새 버전을 적용합니다. apply()가 업무 키에 대해 멱등하므로 없는 것만 추가됩니다.
현재 제약 둘
Approval form preset은 테넌트 범위입니다. nexia-apps:apply-template에 --legal-entity를 넘기면 그것들에 대해 실패합니다.
프로세스 템플릿은 이 경로를 전혀 쓰지 않습니다. 고르면 저장되지 않은 BPMN 편집기 문서가 바뀌고, 평범한 정의 저장·배포가 영속화와 짝 결정 배포를 소유합니다. 업무 프로세스를 보세요.
경계 규칙
initializer가 아닙니다. 템플릿은 설계상 생략 가능합니다. 그 행 없이 App이 고장난다면 설치에 속합니다.
demo data가 아닙니다. demo data는 폐기 가능하고 설치 전제 조건이 아니며 --force 없이는 프로덕션에서 거부됩니다.
여기서 아무것도 권한을 부여하지 않습니다. 복사된 행도 평범한 권한 검사를 받습니다.
원본은 사실상 불변입니다. 배포된 key와 version을 고정으로 취급하고, 동작 변경에는 새 버전을 붙이세요.