OneTrueError - Automated exception handling

Griffin.Decoupled – Winforms sample application

I’ve created a small winforms sample application to demonstrate the features of Griffin.Decoupled. I’ve intentionally kept it small to just demonstrate the core features. This post won’t get into the details of how to bootstrap the framework. There are several samples for that. Instead I’ll show how you can put all pieces together using RavenDB as backend.

The application can do the following:

  • Take notes
  • Stores notes in a RavenDB
  • You can update notes
  • You can mark notes as completed

The application uses the following features from Griffin.Decoupled

  • Automatically commits transactions if commands succeed
  • Holds domain events until a transaction is committed
  • The main form listens on domain events and use them to update the UI
  • The main form uses queries to load data
  • The commands uses the IDataStore abstraction

Here’s how the main screen is structured from the Griffin.Decoupled view:

The form is receiving domain events to be able to update the UI. The events are received automatically since we let the IoC container create it. Heres the form definition:

[Component(Lifetime = Lifetime.Singleton)]
public partial class MainForm : Form, ISubscribeOn<NoteCompleted>, ISubscribeOn<NoteCreated>

Since the events can be executed on any thread we need to move to the UI thread by doing this:

public void Handle(NoteCompleted domainEvent)
{
    Invoke(new MethodInvoker(() => RemoveItem(domainEvent.Id)));
}

which of course also will decouple the form handling from the domain event.

Loading items

To load items we’re using queries. We start by populating the combo box with a query looking like this:

public class GetMyActiveNotes : IQuery<IdTitle[]>
{
}

which is assembled by this handler:

[Component]
public class GetActiveNotesExecutor : IExecuteQuery<GetMyActiveNotes, IdTitle[]>
{
    private readonly IDocumentSession _session;

    public GetActiveNotesExecutor(IDocumentSession session)
    {
        _session = session;
    }

    public IdTitle[] Execute(GetMyActiveNotes query)
    {
        return (from x in _session.Query<Note>()
                where !x.IsCompleted
                select new IdTitle
                    {
                        Id = x.Id,
                        Title = x.Title
                    }).ToArray();
    }
}

Our form loads the items by using a child inversion of control container:

private void PopulateNotes()
{
    CboNote.Items.Add("");
    CboNote.Items.Add(new IdTitle { Id = "-1", Title = "(Create new note)" });

    using (var scope = Program.CreateScope())
    {
        var query = new GetMyActiveNotes();
        var result = scope.Resolve<IQueryDispatcher>().Execute(query);
        foreach (var note in result)
        {
            CboNote.Items.Add(note);
        }
    }
}

Creating items

Creating items are made using a new form.

The form itself is very simple:

[Component]
public partial class CreateNoteForm : Form
{
	public CreateNoteForm()
	{
		InitializeComponent();
	}

	private void btnSave_Click(object sender, EventArgs e)
	{
		// you should validate here too.
		var command = new CreateNote(Title.Text, Body.Text);
		CommandDispatcher.Dispatch(command);
		Close();
	}

	private void btnCancel_Click(object sender, EventArgs e)
	{
		Close();
	}
}

As you can see, it executes the following command:

public class CreateNote : CommandBase
{
    public CreateNote(string title, string body)
    {
        if (title == null) throw new ArgumentNullException("title");
        if (body == null) throw new ArgumentNullException("body");
        Title = title;
        Body = body;
    }

    public string Title { get; private set; }
    public string Body { get; private set; }
}

which is defined as:

[Component]
public class CreateNoteHandler : IHandleCommand<CreateNote>
{
    private readonly INoteStorage _storage;

    public CreateNoteHandler(INoteStorage storage)
    {
        _storage = storage;
    }

    public void Invoke(CreateNote command)
    {
        var note = new Note(command.Title, command.Body);
        _storage.Save(note);
        DomainEvent.Publish(new NoteCreated(note.Id));
    }
}

I normally choose to publish creational domain events from within the commands, since I the got control over them (i.e. send them after the items has been created). For all other domain events I publish them from within the domain entities.

The note storage looks like this:

[Component]
internal class NoteStorage : RavenDataStore<Note>, INoteStorage
{
    public NoteStorage(IDocumentSession session) : base(session)
    {
    }
}

i.e. it doesn’t provide any additional features than the base class / interface.

Changing items

As with creating new items, changes are made by using commands.

The flow is like this:

Combobox is changed, item is loaded

private void CboNote_SelectedIndexChanged(object sender, EventArgs e)
{
    if (CboNote.SelectedIndex == -1)
        return;

    var item = CboNote.Items[CboNote.SelectedIndex] as IdTitle;
    btnComplete.Enabled = false;
    btnSave.Enabled = false;
    Note.Enabled = false;
    Note.Text = "";

    if (item == null)
        return;

    if (item.Id == "-1")
    {
        Program.Build<CreateNoteForm>().Show();
        CboNote.SelectedIndex = -1;
        return;
    }

    LoadItem(item.Id);
}

private void LoadItem(string noteId)
{
    btnComplete.Enabled = btnSave.Enabled = true;
    Note.Enabled = true;

    using (var scope = Program.CreateScope())
    {
        var query = new GetNote(noteId);
        var result = scope.Resolve<IQueryDispatcher>().Execute(query);
        Note.Text = result.Body;
        Note.Tag = result.Id;
    }
}

User make changes to the item

By typing…. ;)

Save button is pressed

private void Save_Click(object sender, EventArgs e)
{
    var cmd = new UpdateNote((string)Note.Tag, Note.Text);
    CommandDispatcher.Dispatch(cmd);
}

Command is executed

public class UpdateNote : CommandBase
{
    public UpdateNote(string id, string newBody)
    {
        if (id == null) throw new ArgumentNullException("id");
        if (newBody == null) throw new ArgumentNullException("newBody");
        Id = id;
        NewBody = newBody;
    }

    public string Id { get; private set; }
    public string NewBody { get; private set; }
}

Command handler

[Component]
public class UpdateHandler : IHandleCommand<UpdateNote>
{
    private readonly INoteStorage _storage;

    public UpdateHandler(INoteStorage storage)
    {
        _storage = storage;
    }

    public void Invoke(UpdateNote command)
    {
        var note = _storage.Load(command.Id);
        note.Update(command.NewBody);
        _storage.Save(note);
    }
}

The “NoteUpdated” event is dispatched

public class Note
{
    //[....]

    public void Update(string newText)
    {
        if (newText == null) throw new ArgumentNullException("newText");
        if (newText.Length > 500000)
            throw new ArgumentOutOfRangeException("newText", newText, "Body may max be 500000 bytes.");

        if (IsCompleted)
            throw new InvalidOperationException("Items may not be changed once completed.");

        var oldText = Body;
        Body = newText;
        UpdatedAt = DateTime.Now;

        DomainEvent.Publish(new NoteUpdated(Id, oldText, newText));
    }
}

Summary

Hope you got a little better understanding about how Griffin.Decoupled works. Do check the sample code since it would probably give you a better picture of what’s going on.

I’ve promised to show how you can use javascript in an ASP.NET MVC/WebApi application to execute queries/commands. I’ve not forgot that. I do however want to show you how you can combine Griffin.Decoupled/Griffin.Container/Griffin.Networking to create a native client which executes all commands/queries on a remote server. That’s why this post took some time (I’ve updated Griffin.Networking to make it possible).

This entry was posted in Uncategorized. Bookmark the permalink.
  • http://www.facebook.com/roberto.lapolli Roberto Lapolli

    such an example in mvc?

  • Saan

    Hi. I love your series on Griffin.Decoupled and I am thinking of using it in my next web project. The one thing I am having issues with prototyping is after creating an item, getting the Id of the item that was just created (have a legacy database – Guids aren’t an option unfortunately) so I can redirect the user to the item’s page. In the summary, you mentioned a MVC example? Do you have a link to that, or just some tips on the best way of handling the ItemCreated event in a web application. Thanks for the great series.