GithubHelp home page GithubHelp logo

andrei15193 / fluxbase Goto Github PK

View Code? Open in Web Editor NEW
0.0 0.0 0.0 264 KB

Provides core types and functionality to implement applications using Flux architecture with .NET.

Home Page: https://andrei15193.github.io/FluxBase

License: GNU Lesser General Public License v3.0

C# 100.00%
flux flux-application-architecture flux-architecture front-end library library-free

fluxbase's Introduction

Andrei15193's profile

Hey, how's it going? Hopefully all is good and if not, what is life without a little challenge?

I'm a software developer since 2013, that's when I got my first job, starting with .NET and SQL for the first 6 years and afterwards added ReactJS to my toolbelt, some DevOps here and there.

I do pet projects apart from what I'm doing at work. I like to have control over what I learn rather than wait for a project where I can learn what I am interested in. Obviously, I wouldn't be able to apply what I learn at the level of a multi-person project with allocated time, but it does provide useful experience later on to help the work project out. I've done this already on multiple occasions.

Besides software development, I read books, mostly non-fiction, related to psychology or phylosophy. The two go hand in hand to a certain degree. Watch TV shows (Pickle Rick.. YEAH!), and movies when I get the time.

I am maintaining two open-source projects, one of the is react-model-view-viewmodel and the other is CodeMap along side an associated project EmbeddedResourceBrowser for it. If you are interested, check them out.

If you want to collaborate or reach out to me, you can look me up on the various social media platforms, if it's something more professional use LinkedIn, otherwise you can send an e-mail or reach out on Facebook or Discord (yes, I am intentionally not leaving any links to these here, you'll have to use your search skills).

fluxbase's People

Contributors

andrei15193 avatar

Watchers

 avatar  avatar

fluxbase's Issues

Add Middleware

Middleware is a great way to extend the functionality for dispatching actions to reduce code duplication and address global concerns such as logging or error reporting.

Each registered middleware object receives the action that is being dispatched and may decide to wrap it in a try.. catch clause (error reporting), just log the action being dispatched as JSON (logging), split the dispatch into multiple dispatches if the payload of an action is a task.

Project Site

GitHub allows project (static) sites to be hosted from the related repository. This is great because it allows a project to have its own site at zero costs where the documentation and tutorials can be hosted. The site itself can use any kind of HTML, CSS and JavaScript meaning that it can be customised in any desirable way.

Currently, the documentation and tutorials are hosted on the project wiki, while this is a good place to start it doesn't offer enough customisation options. For instance, all wiki pages are constrained to the file system naming restrictions, more specifically we cannot use the < and > characters that are common for generics in most, if not all, languages. There is no search feature, we cannot add dropdowns or similar controls for picking documentation versions and so on.

To enhance the documentation page, along side the tutorials page, the project should have its own site hosted by GitHub which allows users to search through the documentation and pick different versions of it. These are the basic features that are required.

Generating documentation should be easy using Andrei15193 / CodeMap with a custom visitor to do so. It would provide an excellent "real world" test case for it.

Ensure Library is CLS Compliant

In order to ensure library compliance it should be marked as CLS Compliant. This will ensure that code that is non-CLS compliant is not made part of the library and will make sure that any language following the Common Language Specifications (CLS) will be able to use the library.

Store Action Handler Lookup

For version 2 only public methods will be looked up to handle store changes. Initially it seemed like a good idea to have all methods that take an ActionData parameter mapped as action handler regardless if they are public or not.

This is somewhat odd because we can do this with reflection, but we cannot do it normally (as in writing code that makes the actual call). The general guideline should be that if we can do something as if we were to write actual code then it should be alright to do generalise some boilerplate code using reflection. In this case, mapping action handlers using reflection should only work for action handlers we can call outside the assembly (public members only).

Going through reflection when otherwise it is not possible is more of a hack than an actual implementation (risking reflection abuse even). We can set private fields through reflection, does that mean we should be doing it or that it is alright?

While this will expose the methods that handle actions they will not be visible in XAML as only properties will be visible. In order to avoid "accidentally" calling the handler one can define an interface for the store that only exposes the data properties, this can help with unit testing as well as interfaces can be mocked by default (in case UI components need to be unit tested, this is unlikely as unit testable logic in the UI is a code smell and should either be moved elsewhere or is an edge case).

This needs to be done for Version 1 as well, only public methods need to be considered as action handlers by default. This behaviour can be overridden at any point and include non public methods as well.

  • Updated action handler lookup
  • Update documentation in code
  • Update documentation on site
  • Update release notes
  • Release version 1.0.1

Add Support for Forms

Most, if not all, applications that have a UI deal with forms one way or another. It would be great to have all the necessary types to interact with forms provided by the library, they are not mandatory for use, but they would make it a lot more easier to start using FluxBase rather than implementing your own.

Remove Dispatch from Middleware Context

Currently (in beta1 and beta2) you can skip all the remaining pipeline elements by dispatching an action directly and not calling context.Next or context.NextAsync. This goes against the idea of a pipeline because the middleware handler can skip all following middleware (skip the pipeline).

The most control a middleware handler has over the action is whether it continues in the pipeline (acts as a filter) or continues in the pipeline either with the same action or a different one. A middleware handler may invoke to next handlers multiple times by calling context.Next or context.NextAsync multiple times (this can be the case of expanding actions that are in fact a Task to notify when the operation starts and when it completes).

Update Build Targets

Remove build targets for versions of framework that are no longer supported and add targets for new versions.

Merge Dispatchers

For the current release (2.0.0) there are two dispatchers, a synchronous one and an asynchronous one. There are two implementations because there is no way to make an adapter to asynchronous middleware from a synchronous one or vice-versa.

This can get confusing and is only done because the middleware needs two execution modes, sync and async. Writing middleware should be rare and reusable, most likely anyone implementing middleware will be implementing both a synchronous one and an asynchronous counterpart.

To simplify this, there should be only one dispatcher implementation that exposes both a synchronous dispatch method and an asynchronous one. A middleware must have both synchronous and asynchronous implementations, this will increase the complexity of a middleware element but as stated, writing middleware should not happen often and is rather a reusable portion of code that will most likely implement both flows.

The dispatcher will handle both sync and async flows where when calling a sync dispatch will go only through the sync middleware implementation and an async dispatch will go only through the async middleware implementation. This will make the usage of a dispatcher a lot easier and have both sync and async methods available in all cases.

Reasoning for the Initial Split

The problem boils down to "blocking" the asynchronous call until it completes when we are using the synchronous method. This is impossible because all dispatches must be carried out on the UI thread (when a dispatch is initiated from the UI thread, this is the expected behaviour) in order execute action handlers (stores) on the UI thread that eventually notify the UI components and update themselves (through binding expressions).

This restriction implies that a synchronous dispatch initiated on the UI thread must complete after the action was handled by all registered stores (or action handlers). If along the way we have an asynchronous operation (e.g.: an async middleware) then control is returned from the async method before it completes and that async middleware may dispatch actions that need to be handled on the UI thread.

Waiting for the async middleware to execute on the UI thread creates a deadlock because all methods (and fragments resulting from async transformation by the compiler) execute on the UI thread and the method blocking the execution by waiting the async middleware will actually wait until a method fragment completes that is placed after itself in the execution queue. The fragment does not execute until the method waiting for it completes.

A synchronous method can be adapted to be asynchronous to some extent. For instance, we can create a TaskCompletionSource to create the task result of the asynchronous adapter and simply call the synchronous method.

public Task MethodAsync()
{
    var taskCompletionSource = new TaskCompletionSource<object>();
    try
    {
        Method(); // The synchronous method we are adapting
        taskCompletionSource.SetResult(null); // Can be the result from the sync method
    }
    catch (Exception exception)
    {
        taskCompletionSource.SetException(exception);
    }
    return taskCompletionSource.Task;
}

Unfortunately we cannot adapt our synchronous middleware to async one because if we want to continue to the next middleware element in the pipeline we need to call context.Next() which is synchronous in this case. This is the same issue, context.Next() should needs to adapt asynchronous middleware from the pipeline in the same meaner a synchronous dispatch needs to adapt asynchronous middleware. It cannot be done since all code needs to execute on the UI thread by default.

To keep the implementation of a middleware element simple, they were split in two, one for sync flows and the other for async flows. This led to two dispatchers, one that is synchronous and one that is asynchronous because neither middleware implementation can be adapted to the other which only made it more complicated from the usage perspective.

Expose Interface for WaitFor Methods

Currently the dispatcher only implements one interface. The IDispatcher which exposes all three Dispatch methods (sync and async) which is great for objects that dispatch actions.

On the other hand, action handlers or stores should be able to use any of the WaitFor methods if they need to wait for a particular handler or store to complete. Currently there is no way of doing this without using the Dispatcher implementation in the stores. This isn't great because the dispatcher cannot be mocked as a dependency in case of unit tests.

Expose an interface for all the WaitFor methods only that can be consumed by action handlers or stores. An extra step that can be done to further decouple direct dependency between stores is to expose yet another interface that explicitly waits for specific stores to complete. E.g.:

interface IStoreWaitHandler
{
    void WaitForMyStore();
}

class StoreWaitHandler : IStoreWaitHandler
{
    private readonly IDispatchWaitHandler _dispatchWaitHandler;
    private readonly MyStore _myStore;

    public StoreWaitHandler(IDispatchWaitHandler dispatchWaitHandler, MyStore myStore)
    {
        _dispatchWaitHandler = dispatchWaitHandler;
        _myStore = myStore;
    }

    public void WaitForMyStore()
        => _dispatchWaitHandler.WaitFor(_myStore);
}

class MyStore : Store
{
    public void Handle(MyAction action)
    {
        // ...
    }
}

class MySecondStore : Store
{
    private readonly IStoreWaitHandler _storeWaitHandler;

    public MyStore(IStoreWaitHandler storeWaitHandler)
    {
        _storeWaitHandler = storeWaitHandler;
    }

    public void Handle(MyAction myAction)
    {
        _storeWaitHandler.WaitForMyStore();
        // ...
    }
}

We can use more fancy implementations using generics and the IoC container and have something like _storeWaitHandler.WaitFor<MyStore>() instead of adding a separate method for each store we want to wait for. This second approach is more cohesive as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.