Skip to content

A library for implementing the Command Pattern, providing of a set of base classes and an invoker class. Useful for abstracting data access and API calls as either queries (for read operations) or commands (for write operations).

License

Notifications You must be signed in to change notification settings

belal-m/Magneto

 
 

Repository files navigation

Build status Join the chat at https://gitter.im/magneto-project License

NuGet NuGet

Magneto

A library for implementing the Command Pattern, providing of a set of base classes and an invoker class. Useful for abstracting data access and API calls as either queries (for read operations) or commands (for write operations).

Define a query object:

public class PostById : AsyncQuery<HttpClient, Post>
{
    public override async Task<Post> ExecuteAsync(HttpClient context)
    {
        var response = await context.GetAsync($"https://jsonplaceholder.typicode.com/posts/{Id}");
        return await response.Content.ReadAsAsync<Post>();
    }
    
    public int Id { get; set; }
}

Invoke it:

var post = await _invoker.QueryAsync(new PostById { Id = 1 });

Mock the result in a unit test:

// Moq
invokerMock.Setup(x => x.QueryAsync(new PostById { Id = 1 })).ReturnsAsync(new Post());
// NSubstitute
invokerMock.QueryAsync(new PostById { Id = 1 }).Returns(new Post());

Leverage built-in caching by deriving from a base class:

public class CommentsByPostId : AsyncCachedQuery<HttpClient, MemoryCacheEntryOptions, Comment[]>
{
    protected override void ConfigureCache(ICacheConfig cacheConfig) => cacheConfig.VaryBy = PostId;
    
    protected override MemoryCacheEntryOptions GetCacheEntryOptions(HttpClient context) =>
        new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(30));

    protected override async Task<Comment[]> QueryAsync(HttpClient context)
    {
        var response = await context.GetAsync($"https://jsonplaceholder.typicode.com/posts/{PostId}/comments");
        return await response.Content.ReadAsAsync<Comment[]>();
    }
    
    public int PostId { get; set; }
}

Cache intermediate results and transform to a final result:

public class UserById : AsyncTransformedCachedQuery<HttpClient, DistributedCacheEntryOptions, User[], User>
{
    protected override DistributedCacheEntryOptions GetCacheEntryOptions(HttpClient context) =>
        new DistributedCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(30));
    
    protected override async Task<User[]> QueryAsync(HttpClient context)
    {
        var response = await context.GetAsync("https://jsonplaceholder.typicode.com/users");
        return await response.Content.ReadAsAsync<User[]>();
    }
    
    protected override Task<User> TransformCachedResultAsync(User[] cachedResult) =>
        Task.FromResult(cachedResult.Single(x => x.Id == Id));
    
    public int Id { get; set; }
}

Register a decorator to apply cross-cutting concerns:

// In application startup
services.AddSingleton<IDecorator, ApplicationInsightsDecorator>();

// Decorator implementation
public class ApplicationInsightsDecorator : IDecorator
{
    readonly TelemetryClient _telemetryClient;

    public ApplicationInsightsDecorator(TelemetryClient telemetryClient) =>
        _telemetryClient = telemetryClient;

    public T Decorate<T>(object operation, Func<T> invoke)
    {
        try
        {
            var stopwatch = Stopwatch.StartNew();
            var result = invoke();
            var elapsed = stopwatch.Elapsed.TotalMilliseconds;
            _telemetryClient.TrackMetric(operation.GetType().FullName, elapsed);
            return result;
        }
        catch (Exception e)
        {
            _telemetryClient.TrackException(e);
            throw;
        }
    }

About

A library for implementing the Command Pattern, providing of a set of base classes and an invoker class. Useful for abstracting data access and API calls as either queries (for read operations) or commands (for write operations).

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 100.0%