使用供应商事件调度程序时如何在域上下文中使用依赖项

时间:2016-08-01 17:30:19

标签: php events domain-driven-design

我正在考虑将DDD用于示例应用程序,现在我仍然坚持使用域事件。由于那里有非常好的EventDispatcher,我不想重新发明轮子。但是这些实现都要求我的事件正在实现它们的EventInterface。我想保持我的域事件的实现与任何类型的实现分开。我该如何处理这个问题?

2 个答案:

答案 0 :(得分:1)

这是我的意思:

/// <summary>
/// Contains the contract for publishing domain events.
/// </summary>
public interface IDomainEventPublisher
{
    /// <summary>
    /// Publishes the domain events.
    /// </summary>
    /// <param name="domainEvents">events to be published</param>
    Task Publish(IEnumerable domainEvents);

    /// <summary>
    /// Subscribes the list of subscribers.
    /// </summary>
    /// <param name="subscribers">event subscriptions</param>
    void Subscribe(params IDomainEventSubscription[] subscribers);

    /// <summary>
    /// Subscribes to the event.
    /// </summary>
    /// <typeparam name="T">event to subscribe too</typeparam>
    /// <returns>event subscription</returns>
    DomainEventSubscription<T> SubscribeTo<T>();
}

/// <summary>
/// Publishes events to registered subscribers.
/// </summary>
public class DefaultDomainEventPublisher : IDomainEventPublisher
{
    readonly List<IDomainEventSubscription> subscriptions;

    /// <summary>
    /// Creates a new instance of the object.
    /// </summary>
    public DefaultDomainEventPublisher()
    {
        subscriptions = new List<IDomainEventSubscription>();
    }

    /// <summary>
    /// Subscribes to the event.
    /// </summary>
    /// <typeparam name="T">event to subscribe too</typeparam>
    /// <returns>event subscription</returns>
    public DomainEventSubscription<T> SubscribeTo<T>()
    {
        var subscription = new DomainEventSubscription<T>();

        subscriptions.Add(subscription);

        return subscription;
    }

    /// <summary>
    /// Subscribes the list of subscribers.
    /// </summary>
    /// <param name="subscribers">event subscriptions</param>
    public void Subscribe(params IDomainEventSubscription[] subscribers)
    {
        subscriptions.AddRange(subscribers);
    }

    /// <summary>
    /// Publishes the domain events.
    /// </summary>
    /// <param name="domainEvents">events to be published</param>
    public virtual async Task Publish(IEnumerable domainEvents)
    {
        foreach (var @event in domainEvents)
        {
            var subscribers = subscriptions.Where(s => s.CanHandleType(@event.GetType()));

            foreach (var subscriber in subscribers)
            {
                await subscriber.Handle(@event);
            }
        }
    }
}

/// <summary>
/// Handles the subscription services for an event.
/// </summary>
/// <typeparam name="T"></typeparam>
public class DomainEventSubscription<T> : IDomainEventSubscription
{
    readonly List<object> subscriptionMethods;

    /// <summary>
    /// Constructs a new instance.
    /// </summary>
    public DomainEventSubscription()
    {
        this.subscriptionMethods = new List<object>();
    }

    /// <summary>
    /// Adds the subscription method to the subscription.
    /// </summary>
    /// <param name="subscriptionMethod">subscription method</param>
    public void AddSubscriptionMethod(ISubscriptionMethod subscriptionMethod)
    {
        subscriptionMethods.Add(subscriptionMethod);
    }

    /// <summary>
    /// Returns whether or not the subscription can handle the specified type.
    /// </summary>
    /// <param name="type"></param>
    /// <returns>whether it can handle the type</returns>
    public bool CanHandleType(Type type)
    {
        return type.IsAssignableFrom(typeof(T));
    }

    /// <summary>
    /// Publishes the event.
    /// </summary>
    /// <param name="event">event to publish</param>
    public async Task Handle(object @event)
    {
        foreach (var subscriptionMethod in subscriptionMethods)
        {
            await (subscriptionMethod as dynamic).Handle(@event);
        }
    }
}

/// <summary>
/// Contains the contract for event subscribers.
/// </summary>
public interface IDomainEventSubscription
{
    /// <summary>
    /// Publishes the event.
    /// </summary>
    /// <param name="event">event to publish</param>
    Task Handle(object @event);

    /// <summary>
    /// Returns whether or not the subscription can handle the specified type.
    /// </summary>
    /// <param name="type"></param>
    /// <returns>whether it can handle the type</returns>
    bool CanHandleType(Type type);
}

/// <summary>
/// Contacts the contract for subscription methods.
/// </summary>
public interface ISubscriptionMethod
{
    /// <summary>
    /// Publishes the event.
    /// </summary>
    /// <param name="event">event to publish</param>
    Task Handle(object @event);
}

/// <summary>
/// Base class for subscription method implementations.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class SubscriptionMethod : ISubscriptionMethod
{
    /// <summary>
    /// Publishes the event.
    /// </summary>
    /// <param name="event">event to publish</param>
    public abstract Task Handle(object @event);
}

/// <summary>
/// Publishes events using delegates.
/// </summary>
/// <typeparam name="T"></typeparam>
public class DelegateSubscriptionMethod<T> : ISubscriptionMethod
{
    readonly Func<T, Task> delegateAction;

    /// <summary>
    /// Constructs a new instance.
    /// </summary>
    /// <param name="action">delegate used for publishing</param>
    public DelegateSubscriptionMethod(Func<T, Task> action)
    {
        delegateAction = action;
    }

    /// <summary>
    /// Publishes the event using a delegate.
    /// </summary>
    /// <param name="event">event to publish</param>
    public async Task Handle(object @event)
    {
        await delegateAction(@event as dynamic);
    }
}

/// <summary>
/// Provides an extension method for publishing an event using a delegate.
/// </summary>
public static class DomainEventSubscriptionExtensions
{
    /// <summary>
    /// Adds an event subscription for publishing using the specified delegate.
    /// </summary>
    /// <typeparam name="T">event the subscription is subscribed too</typeparam>
    /// <param name="subscription">event subscription</param>
    /// <param name="action">delegate used for publishing</param>
    /// <returns>event subscription</returns>
    public static DomainEventSubscription<T> UsingDelegate<T>(this DomainEventSubscription<T> subscription, Func<T, Task> action)
    {
        var subscriptionMethod = new DelegateSubscriptionMethod<T>(action);

        subscription.AddSubscriptionMethod(subscriptionMethod);

        return subscription;
    }
}

以下是一些基本用法:

publisher.SubscribeTo<DocumentOwnerChanged>()
    .UsingDelegate(
        async a => await messageGateway.DocumentOwnerChanged(1, 1, 1));

我还有一个使用我的DI容器自动订阅的实现。如果你想看到这个,请告诉我。

答案 1 :(得分:1)

我能想到的两种可能性

1)在EventDispatcher前面介绍适配器的概念;适配器接受域生成的格式的事件,&#34;序列化&#34;将相同的数据放入您正在使用的特定调度程序实现所需的表单中。

2)使用builder api创建域事件;域定义了构建器合同,但是封面下的实现特定于您正在使用的事件调度程序。