What's new in Downlink 0.2: Extensibility

This is part of a series of posts on the new features added in Downlink 0.2! Check out this post for more or hit the docs.

While hosting Downlink (as we discussed yesterday) might be the big-ticket change in the rebuilt version, it wouldn't have been possible without this change: Downlink is now completely extensible!

That's right, you can now control and modify Downlink on the fly in your own code using any of a myriad of extension points. In fact, when self-hosting you get direct access to the services container it uses (shared with the hosting app) so you can register and resolve any service.

That's all good, but what does that look like in practice? Let's say you wanted to change how Downlink matches patterns in your app: simply implement the IPatternMatcher interface in a class (that we'll call MyAwesomeMatcher), and then in your startup code:

services.AddMvc().AddDownlink(d => d.AddPatternMatcher<MyAwesomeMatcher>());

Now, you can update your configuration and Downlink will automatically make sure your pattern matcher is available.

Dependency Injection

Since Downlink's extensibility is built around dependency injection, you can also use injection in your own extensions. Let's say that awesome pattern matcher from above needs access to the app configuration for some reason. Well, just add it to your constructor and Downlink will resolve it for you:

public class MyAwesomeMatcher : IPatternMatcher {
  public MyAwesomeMatcher(IConfiguration config) {
    // ...
  }
  // TRIMMED
}

If you need something that's not loaded by default, just add it in the startup code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().AddDownlink(d => {
        d.Services.AddSingleton<MyExtraService>();
        d.AddPatternMatcher<MyAwesomeMatcher>();
    });
}

Now you can resolve MyExtraService in your constructor!

Disabling built-ins

Downlink out of the box includes a lot of "default" implementations for moving parts like storage backends, scheme clients and pattern matchers. If you don't want those active in your app, you can use the new extensibility to also disable them in your startup code:

services.AddMvc().AddDownlink(
  d => d.AddPatternMatcher<MyAwesomeMatcher>(),
  DownlinkBuilderOptions.SkipDefaultPatterns
);
// now only your pattern matcher is active!

Use Cases

In reality, almost every moving part of Downlink can be replaced or controlled using the extensibility points in Downlink, but the main ones (which have helpers provided) include:

  • Storage backends
  • Pattern matchers
  • Scheme clients
  • Route builders
  • Plugins (more on that on Friday!)

Note that the pre-built app (as included in Docker) has experimental plugin support for extensibility instead, but you'll need to wait for the post on plugins to see that in action!

The technical details

Okay, so for all the fancy talk this really comes down to a particularly heavy refactoring of the app code to be interface-driven and almost completely reliant on DI and IoC.

Basically every moving part in Downlink now implements an interface that gets resolved out of the DI container (currently using Microsoft.Extensions.DependencyInjection) and the app startup code registers all the sane defaults using a plugin. This way, self-hosting scenarios can replace any Downlink component by registering it with the DI container in the AddDownlink method!


You can find detailed documentation on Downlink's new extensibility in the developer guide (or check out all the online docs)

Comments

comments powered by Disqus