이벤트로 레거시 코드 풀기
4587 단어 refactoringdesignarchitecture
레거시 코드는 일반적으로 다양한 개념과 추상화 수준을 혼합하는 긴 방법이 특징입니다. 이러한 맥락에서 동작을 적절한 모듈로 추출하는 것은 어려울 수 있습니다. 모든 것이 모든 것에 의존하는 것처럼 느껴지고 전체 코드베이스를 대대적으로 점검해야 하기 때문입니다. 이 문서에서는 기존 코드를 추출하거나 새 코드를 추가하기 위해 레거시 코드와 적절하게 바인딩된 다른 모듈 간의 결합을 줄이기 위해 이벤트를 사용할 것을 제안합니다.
UserManager
메서드를 특징으로 하는 일반적인 CreateUser
클래스가 있다고 가정해 보겠습니다. 사용자가 생성되면 비밀번호를 설정하고 어딘가에서 캐시를 지우기 위해 환영 이메일을 보낼 수 있습니다. 초기 코드는 다음과 같습니다.public class UserManager {
// ...
public async Task CreateUser(string username) {
var user = new User { Username = username };
int userId = await _repository.Add(user);
await ClearCacheForUser(userId);
await SendWelcomeEmail(userId, user);
}
private async Task ClearCacheForUser(int userId) {
// ...
}
private async Task SendWelcomeEmail(int userId, User user) {
// ...
}
}
첫 번째 단계는
ClearCacheForUser
및 SendWelcomeEmail
를 전용 클래스로 추출하는 것입니다. 그러나 근본적으로 문제를 바꾸지는 않을 것입니다. 실제로 CreateUser
메서드는 여전히 캐시를 지우고 이메일을 보내고 사용자 생성 시 발생해야 하는 다른 모든 프로세스를 알아야 합니다. 따라서 커플 링은 여전히 존재합니다.이 결합을 제거하는 간단한 솔루션은 이벤트를 사용하는 것입니다. 눈살을 찌푸리기 전에 이벤트를 사용하는 것이 반드시 이벤트 스토어 등으로 완전한 이벤트 소싱 아키텍처를 구현하는 것은 아니라는 점에 유의해야 합니다(이벤트 기반 및 이벤트 소싱은 two separate things ). 사실, 매우 간단한 프로세스 내 이벤트 버스로 시작하면 레거시 코드를 푸는 데 큰 도움이 될 수 있습니다.
다음 코드는 사용할 수 있는 프로세스 내 이벤트 버스의 완전한 구현입니다.
public abstract class MyEvent {}
public interface IEventBus
{
Task Publish<T>(T @event) where T : EpEvent;
void Subscribe<T>(IEventHandler<T> handler) where T : MyEvent;
}
public interface IEventHandler<in T> where T : MyEvent
{
Task Handle(T @event);
}
public class InProcessEventBus : IEventBus
{
private readonly IDictionary<Type, IList<object>> _subscriptions = new Dictionary<Type, IList<object>>();
public async Task Publish<T>(T @event) where T : MyEvent
{
if (_subscriptions.TryGetValue(typeof(T), out IList<object> handlers))
{
foreach (IEventHandler<T> handler in handlers.OfType<IEventHandler<T>>())
{
await handler.Handle(@event);
}
}
}
public void Subscribe<T>(IEventHandler<T> handler) where T : MyEvent
{
if (!_subscriptions.ContainsKey(typeof(T)))
{
_subscriptions[typeof(T)] = new List<object>();
}
_subscriptions[typeof(T)].Add(handler);
}
}
이 최소한의 이벤트 버스를 사용하여
CreateUser
메서드를 다음과 같이 다시 작성할 수 있습니다.public class UserCreatedEvent : MyEvent {
public int UserId { get; }
public string Username { get; }
public UserCreatedEvent(int userId, string username) {
UserId = userId;
Username = username;
}
}
public class UserManager {
// ...
private readonly IEventBus _eventBus;
public async Task CreateUser(string username) {
var user = new User { Username = username };
int userId = await _repository.Add(user);
await _eventBus.Publish(new UserCreatedEvent(userId, username));
}
}
이제 핸들러를 생성하여 이를 처리하고
UserCreatedEvent
필요한 작업을 수행할 수 있습니다.public class ClearUserCacheEventHandler : IEventHandler<UserCreatedEvent>
{
public ClearUserCacheEventHandler(IEventBus eventBus)
{
eventBus.Subscribe(this);
}
public Task Handle(UserCreatedEvent @event)
{
// Clear cache
}
}
public class SendWelcomeEmailEventHandler : IEventHandler<UserCreatedEvent>
{
public SendWelcomeEmailEventHandler(IEventBus eventBus)
{
eventBus.Subscribe(this);
}
public Task Handle(UserCreatedEvent @event)
{
// Send welcome email
}
}
이 이벤트 버스는 이벤트가 동기식으로 처리되고
Publish
메서드는 모든 이벤트가 처리된 후에만 반환하므로 확장성에 도움이 되지 않습니다. 그러나 코드베이스 내에서 결합을 크게 줄일 수 있으며 아키텍처를 보다 이벤트 중심으로 만들기 위한 디딤돌 역할을 할 수 있습니다. 또한 더 나아가야 하는 경우(예: 이벤트를 통해 통신하는 두 서비스) out-of-process 구현을 위해 InProcessEventBus
를 전환할 수 있는 추상화가 이미 있습니다.
Reference
이 문제에 관하여(이벤트로 레거시 코드 풀기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/rnowif/untangling-legacy-code-with-events-47nb텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)