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:
IModel
as a base. You can use a set of attributes to steer the behavior of generated code.[Tools]->[UMVR]->[Generator]
-> Generate essentials.IFoo
)
FooModel
+ FooFactory
- implementation of provided interface, packed with UniRx IObservable
properties.FooReactor
- every FooModel
causes one to be created automatically and is passed as a parameter to it's Reactor
. 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 the Model
properties are observable, you can perform any stream shenanigans you want.FooRepository
- sometimes one single Model
doesn't cut it when you want to describe something collection based. You could put an IList<IFoo>
into another type, but that's not necessary - you can instead inject IRepository<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 with FooInstallerBase.Install(Container)
in your own code.Views
are written manually, Zenject will deliver them to Reactors
[Tools]->[UMVR]->[Generator]
Model
in Reactors
to avoid circular dependencies. It's probably better to write separate Controllers
to drive the logic and modify Model
.Views
don't have to touch Model
directly - there's ICommand
interface for that.IDisposable
for subscription cleanup, it trickles down to UMVR as well. Most types are IDisposable
so you can dispose your subscriptions easily. Bind to IDisposable
in Zenject to automate that.