DxMessaging + Reflex¶
Overview¶
Use Reflex for object construction and DxMessaging for message delivery. The optional DxMessaging Reflex assembly registers both MessageBus and IMessageBus, and it can provide IMessageRegistrationBuilder to container-created services.
Changed in v3.2.3: The examples target Reflex 14.0 or newer. DxMessaging's registration helpers also adapt to the pre-14
AddSingletonAPI.
Quick start¶
Prerequisites¶
- Install DxMessaging through UPM.
- Install Reflex 14.0 or newer (
com.gustavopsantos.reflex). - Create a Reflex settings asset inside a
Resourcesfolder with Assets > Create > Reflex > Settings.
Create an installer¶
Reflex discovers IInstaller components below a ContainerScope. Derive the installer from MonoBehaviour so it can be attached to that hierarchy.
using DxMessaging.Unity.Integrations.Reflex;
using Reflex.Core;
using UnityEngine;
public sealed class DxMessagingInstaller : MonoBehaviour, IInstaller
{
public void InstallBindings(ContainerBuilder builder)
{
builder.AddDxMessagingBus();
new DxMessagingRegistrationInstaller().InstallBindings(builder);
}
}
AddDxMessagingBus() uses an explicit factory and exposes the same singleton as both MessageBus and IMessageBus. DxMessagingRegistrationInstaller adds IMessageRegistrationBuilder.
Add it to a scope¶
- Create a scene scope with GameObject > Reflex > SceneScope.
- Add
DxMessagingInstallerto theSceneScopeGameObject or one of its children. - Enter Play Mode. Reflex builds the scene container and injects scene objects from it.
For one bus shared across scenes, put the installer on a Reflex RootScope prefab and add that prefab to the RootScopes list in the Reflex settings asset.
Register a service¶
The following service owns a MessageRegistrationLease. Its constructor stages a real handler, Initialize() activates it, and Dispose() releases it.
using System;
using DxMessaging.Core.Attributes;
using DxMessaging.Core.MessageBus;
[DxBroadcastMessage]
[DxAutoConstructor]
public readonly partial struct PlayerDamaged
{
public readonly int damage;
}
public sealed class DamageService : IDisposable
{
private readonly MessageRegistrationLease _lease;
public DamageService(IMessageRegistrationBuilder registrationBuilder)
{
MessageRegistrationBuildOptions options = new()
{
Configure = token =>
{
_ = token.RegisterBroadcastWithoutSource<PlayerDamaged>(OnPlayerDamaged);
},
};
_lease = registrationBuilder.Build(options);
}
public int LastDamage { get; private set; }
public void Initialize()
{
_lease.Activate();
}
public void Dispose()
{
_lease.Dispose();
}
private void OnPlayerDamaged(DxMessaging.Core.InstanceId player, PlayerDamaged message)
{
LastDamage = message.damage;
}
}
Register the service with Reflex 14's singleton and lazy-resolution settings:
using Reflex.Enums;
builder.RegisterType(
typeof(DamageService),
Lifetime.Singleton,
Resolution.Lazy
);
Reflex disposes singleton services with their owning container. Call Initialize() from a bootstrap component after resolving the service. Reflex does not provide an IInitializable lifecycle contract.
Configure an existing MessagingComponent¶
Inject the container bus before MessagingComponent registers its handlers:
using DxMessaging.Core.MessageBus;
using DxMessaging.Unity;
using Reflex.Attributes;
using UnityEngine;
[DisallowMultipleComponent]
[RequireComponent(typeof(MessagingComponent))]
public sealed class MessagingComponentConfigurator : MonoBehaviour
{
[Inject]
private IMessageBus _messageBus;
private void Awake()
{
GetComponent<MessagingComponent>().Configure(
_messageBus,
MessageBusRebindMode.RebindActive
);
}
}
Add this configurator beside each MessagingComponent that should use the container-owned bus. Reflex's scene scope runs before ordinary Awake() methods and injects the field first.
Inject IMessageBus directly¶
Inject IMessageBus into a component that only emits messages:
using DxMessaging.Core.Extensions;
using DxMessaging.Core.MessageBus;
using Reflex.Attributes;
using UnityEngine;
public sealed class GameBootstrap : MonoBehaviour
{
[Inject]
private IMessageBus _messageBus;
private void Start()
{
GameStarted message = new();
_messageBus.EmitUntargeted(ref message);
}
}
Inject pooled objects¶
Reflex injects scene objects when it creates the scene container. Inject objects instantiated later through GameObjectInjector before returning them to callers:
using System.Collections.Generic;
using Reflex.Core;
using Reflex.Injectors;
using UnityEngine;
public sealed class EnemyPool
{
private readonly Container _container;
private readonly Enemy _enemyPrefab;
private readonly Queue<Enemy> _pool = new();
public EnemyPool(Container container, Enemy enemyPrefab)
{
_container = container;
_enemyPrefab = enemyPrefab;
}
public Enemy Spawn()
{
if (_pool.Count > 0)
{
return _pool.Dequeue();
}
Enemy enemy = UnityEngine.Object.Instantiate(_enemyPrefab);
GameObjectInjector.InjectObject(enemy.gameObject, _container);
return enemy;
}
public void Return(Enemy enemy)
{
_pool.Enqueue(enemy);
}
}
Test with Reflex¶
Build an isolated container with a real bus and the same DxMessaging installer used at runtime:
using DxMessaging.Core.MessageBus;
using DxMessaging.Unity.Integrations.Reflex;
using NUnit.Framework;
using Reflex.Core;
using Reflex.Enums;
[TestFixture]
public sealed class DamageServiceTests
{
[Test]
public void InitializeListensToMessages()
{
ContainerBuilder builder = new();
MessageBus bus = new();
builder.RegisterValue(
bus,
new[] { typeof(MessageBus), typeof(IMessageBus) }
);
new DxMessagingRegistrationInstaller().InstallBindings(builder);
builder.RegisterType(
typeof(DamageService),
Lifetime.Singleton,
Resolution.Lazy
);
using Container container = builder.Build();
DamageService service = container.Resolve<DamageService>();
service.Initialize();
PlayerDamaged message = new(25);
DxMessaging.Core.InstanceId player = new(42);
bus.SourcedBroadcast(ref player, ref message);
Assert.That(service.LastDamage, Is.EqualTo(25));
}
}
Checklist¶
Initial setup¶
- Install DxMessaging and Reflex.
- Create a Reflex settings asset under
Resources. - Create a
ContainerScopeand attach anIInstallercomponent. - Call
AddDxMessagingBus()and installDxMessagingRegistrationInstaller.
Integration¶
- Register container services with a concrete Reflex lifetime and resolution mode.
- Activate builder-created leases from a bootstrap component.
- Dispose leases directly or let Reflex dispose their singleton owner.
- Configure each existing
MessagingComponentwith the injected bus.
Pooling and tests¶
- Inject runtime-created GameObjects with
GameObjectInjectorbefore use. - Build a fresh
ContainerBuilderandMessageBusfor each test. - Register real handlers and assert their observable result.
Next steps¶
- Zenject Integration -- Zenject container wiring
- VContainer Integration -- VContainer lifetime scopes
- Back to Documentation Hub -- all DxMessaging guides