> For the complete documentation index, see [llms.txt](https://docs.axuslab.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.axuslab.com/2.-domain-layer/unit_of_work.md).

# 작업 단위 - Unit Of Work

Unit of Work

#### 설명

연결 및 트랜잭션 관리는 데이터베이스를 사용하는 애플리케이션에서 가장 중요한 개념 중 하나입니다. Axs는 **UOW(UnitOfWOrk)** 시스템을 사용하여 데이터베이스 연결과 트랜잭션을 관리합니다.

![Alt text](/files/wVB7U4yqTu6Q9WmupHcl)

([참고사이트](https://justhackem.wordpress.com/2014/05/25/tdd-using-repository-uow/))

#### 연결과 트랜잭션 관리

Axs는 데이터베이스 연결을 **열고(Open)** **작업 단위 메서드**를 입력 할 때 **트랜잭션**을 **시작**합니다. 이 방법으로 안전하게 연결을 사용할 수 있습니다. 메서드가 끝나면 트랜잭션이 **커밋**되고 연결이 삭제됩니다. 메서드에서 예외가 발생하면 트랜잭션이 **롤백**되고 연결이 **삭제**됩니다.

작업 단위 메서드가 다른 작업 단위 메서드를 호출하면 둘 다 동일한 연결 및 트랜잭션을 사용합니다. 처음 입력 한 방법은 연결 및 트랜잭션을 관리하고 나머지는 이를 재사용합니다.

작업 단위의 기본 **IsolationLevel**은 구성되지 않은 경우 **ReadUncommitted**입니다. 작업 단위 [옵션](#options)을 사용하여 쉽게 구성 할 수 있습니다.

**암묵적인 작업단위 메서드**

아래와 같이 애플리케이션 컨트롤러([application controller](https://github.com/axuslab/docs.axs/blob/main/Developments/Application_Controllers/README.md)) 방법이 있다고 가정합니다.

```csharp
public class SampleAppController : ISampleAppController
{
    private readonly ISampleRepository _sampleRepository;
    private readonly IStatisticsRepository _statisticsRepository;

    public SampleAppController(ISampleRepository sampleRepository, IStatisticsRepository statisticsRepository)
    {
        _sampleRepository = sampleRepository;
        _statisticsRepository = statisticsRepository;
    }

    public void CreateSample(CreateSampleDto input)
    {
        var sample = new Sample { MyName = input.MyName, EmailAddress = input.EmailAddress };
        _sampleRepository.Insert(sample);
        _statisticsRepository.IncrementSampleCount();
    }
}
```

CreateSample 메서드에서 개인 저장소를 사용하는 샘플을 삽입하고 통계 저장소를 사용하여 총 샘플 수를 늘립니다. 애플리케이션 컨트롤러 메서드는 기본적으로 작업 단위이므로 두 저장소 모두 동일한 연결 및 트랜잭션을 **공유**합니다. Axs는 데이터베이스 연결을 열고 CreateSample 메서드를 입력 할 때 트랜잭션을 시작하며 예외가 발생하지 않으면 트랜잭션 끝에서 트랜잭션을 커밋합니다. 예외가 발생하면 모든 것을 롤백합니다.

**UnitOfWork Attribute**

첫 번째이자 선호되는 접근 방식은 **UnitOfWork** 속성을 사용하는 것입니다.

```csharp
[UnitOfWork]
public void CreateSample(CreateSampleDto input)
{
    var sample = new Sample { MyName = input.MyName, EmailAddress = input.EmailAddress };
    _sampleRepository.Insert(sample);
    _statisticsRepository.IncrementSampleCount();
}
```

이렇게하면 CreateSample 메서드가 작업 단위가되어 데이터베이스 연결 및 트랜잭션을 관리합니다. 두 저장소 모두 동일한 작업 단위를 사용합니다.

#### 작업단위 기타 사용 방법

**Unit Of Work 사용 안함**

일반적인 작업 단위 방법에 대해 작업 단위를 비활성화 할 수 있습니다. 이렇게 하려면 UnitOfWorkAttribute의 **IsDisabled** 속성을 사용합니다.

```csharp
[UnitOfWork(IsDisabled = true)]
public virtual void Remove(RemoveDto input)
{
    _otherRepository.Delete(input.Id);
}
```

일부 상황에서는 작업 단위를 비활성화 할 수 있습니다.

**Unit Of Work 트랜잭션 사용 안함**

데이터베이스 수준 트랜잭션을 비활성화 할 수 있습니다.

```csharp
[UnitOfWork(isTransactional: false)]
public GetTasksResult GetTasks(GetTasksDto input)
{
    var tasks = _taskRepository.GetAllWithPeople(input.AssignedId);
    return new GetTasksResult
    {
        Tasks = Mapper.Map<List<TaskDto>>(tasks)
    };
}
```

**자동 반영**

메서드가 작업 단위 인 경우 Axs는 메서드가 끝날 때 모든 변경 사항을 자동으로 저장합니다. 샘플의 이름을 수정하는 코드입니다.

```csharp
[UnitOfWork]
public void UpdateName(UpdateNameDto input)
{
    var sample = _sampleRepository.Get(input.SampleId);
    sample.MyName = input.NewMyName;
}
```

ORM 프레임 워크는 작업 단위에서 엔티티의 모든 변경 사항을 추적하고 이러한 변경 사항을 데이터베이스에 반영합니다. 일반적인 작업 단위 메서드에 대해서는 UnitOfWork 속성을 선언 할 필요가 없습니다.

#### Options

작업 단위의 동작을 변경하는 데 사용할 수있는 몇 가지 옵션이 있습니다.

```csharp
public class SimpleTaskSystemCoreModule : AxsModule
{
    public override void PreInitialize()
    {
        // 작업 단위의 기본 트랜잭션 레벨을 지정한다.
        Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted;
        // 작업 단위 기본 타임아웃 지정
        Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes(30);
    }
}
```

#### Events

작업 단위에는 **Completed**, **Failed** 및 **Disposed** 이벤트가 있습니다. 이러한 이벤트에 등록하고 필요한 작업을 수행 할 수 있습니다. 예를 들어, 현재 작업 단위가 성공적으로 완료되면 일부 코드를 실행할 수 있습니다.

```csharp
public void CreateTask(CreateTaskDto input)
{
    var task = new Task { Description = input.Description };

    if (input.AssignedPersonId.HasValue)
    {
        task.AssignedPersonId = input.AssignedPersonId.Value;
        // 작업단위가 완료가 될 때 실행하는 코드 등록
        _unitOfWorkManager.Current.Completed += (sender, args) => { /* TODO: 이메일 전송 */ };
    }

    _taskRepository.Insert(task);
}
```

#### 확인

UOW를 테스트 시 속성을 통한 사용과 UnitOfWorkManager를 사용하여 각각 테스트를 진행할 수 있습니다.

**스웨거에서 확인**

스웨거에서 아래 주소로 호출하면 테스트를 할 수 있습니다.

**1. 정상 입력 시나리오**

**/api/services/app/Sample/CreateSampleUOW**

![picture 1](/files/0eAnt0CRLm6yZ0UpGNdQ)

데이터가 정상적으로 들어 갔는지 확인은 아래 주소에서 확인하면 됩니다.

**/api/services/app/Sample/GetAll**

![picture 3](/files/aJJuXMPOe6liDJdtoFL0)

데이터 확인

![picture 4](/files/DD37y2lX5loefd3dt28d)

최근에 들어간 데이터를 확인할 수 있습니다.

**2. 에러 발생 입력 시나리오**

**/api/services/app/Sample/CreateSampleUOWEx**

![picture 6](/files/jvDmPWBLt3JDRT89ulLE)

![picture 7](/files/TRfIpwTGIBzfkIRhEV3j)

데이터 확인

에러가 발생이 되어 입력된 데이터가 추가 되지 않은 것을 확인하면 됩니다.

UOW 관련 테스트는 아래 스웨거에서 확인할 수 있습니다.

![picture 5](/files/Xbq7YuDsVkGMC7lULCwD)
