Tl;dr Unity architectural framework built on Zenject and UniRx with the purpose of speeding up development while encouraging well laid out architecture. It heavily relies on code generation. You could compare it to Ruby on Rails, in that it prefers convention over configuration and wants you to automate the repetitive stuff (but then, my experience with RoR is very limited)
The R
in UMVR comes from 3 pillars: Reactors, Repositories and Rails.
Reactor
is just the name for MVPs Presenter
with all the data exposed in form of UniRx data streams. In its current form, one presenter gets created for every model and you bind data manually, more or less like this:
using UniRx;
using UnityEngine;
public partial class FooReactor
{
protected override void BindDataSourceImpl(FooModel model)
{
Subscriptions.Add(model.GetProperty<string>(nameof(IFoo.Text)).Subscribe(Debug.Log));
}
}
This is the second meaning of R in UMVR - Repository
lays out the models in a predictable way. Think of it like of the way to make your model a little more like a database, with each Repository being a table.
You can get by primary key:
Id totallyValidId = default;
repository.Get(totallyValidId);
You can register secondary key and get by it:
public FooRepository(FooReactorFactory fooReactorFactory, FooRepository repository) : base(fooReactorFactory)
{
AddIndex(nameof(IFoo.Text), new SecondaryIndex<string,IFoo>(nameof(IFoo.Text), repository));
}
...
repository.GetBy(nameof(IFoo.Text), "Hello world");
And then there's the obvious stuff: Added/Removed
events, iterators.
So, the inspiration with Ruby on Rails is a thing here. What UMVR tries to provide is a way to focus on design of your game/app and NOT on wiring it up together. You design the data layout and implement the game logic, UMVR puts it all together for you. The standard use case would be:
- Write your interfaces, provide
IModel
as a base. You can use a set of attributes to steer the behavior of generated code. [Tools]->[UMVR]->[Generator]
-> Generate essentials.- You now have a set of tools to be used with your model (lets call it
IFoo
)FooModel
+FooFactory
- implementation of provided interface, packed with UniRxIObservable
properties.FooReactor
- everyFooModel
causes one to be created automatically and is passed as a parameter to it'sReactor
. Use it to react to stuff that happens to your model. The most obvious use would be feeding changes to the view. Note, that since theModel
properties are observable, you can perform any stream shenanigans you want.FooRepository
- sometimes one singleModel
doesn't cut it when you want to describe something collection based. You could put anIList<IFoo>
into another type, but that's not necessary - you can instead injectIRepository<IFoo>
to get an database-like access to an entire model type.FooInstallerBase
- last but not least, all the essentials are already ready to be called withFooInstallerBase.Install(Container)
in your own code.
- Zenject usage is assumed and made easier by automatic generation of installers
Views
are written manually, Zenject will deliver them toReactors
- Rest of the stuff is generated: in Unity,
[Tools]->[UMVR]->[Generator]
- You should not, or at least be careful, modify
Model
inReactors
to avoid circular dependencies. It's probably better to write separateControllers
to drive the logic and modifyModel
. Views
don't have to touchModel
directly - there'sICommand
interface for that.- Since UniRx uses
IDisposable
for subscription cleanup, it trickles down to UMVR as well. Most types areIDisposable
so you can dispose your subscriptions easily. Bind toIDisposable
in Zenject to automate that.