Modularizing Services

ServiceStack only allows a single App Host for each App Domain. As you might be able to infer from the name, the role of the Host project is to be the conduit for binding all your services concrete dependencies, plugins, filters and everything else your service needs. The configuration of your service should be immutable after everything is initialized in your AppHost.Configure() method. The Physical project structure wiki page wiki shows the recommended physical project structure for typical solutions.

Modularizing services in multiple assemblies

Whilst you can only have 1 AppHost, you can have multiple services spread across multiple assemblies. In that case you have to provide a list of assemblies containing the services to the AppHostBase constructor, e.g:

public class AppHost : AppHostBase
{
    //Tell ServiceStack the name of your app and which assemblies to scan for services
    public AppHost() : base("Hello ServiceStack!", 
       typeof(ServicesFromDll1).Assembly,
       typeof(ServicesFromDll2).Assembly
       /*, etc */) {}

    public override void Configure(Container container) {}
}

You can also provide your own strategy for discovering and resolving the service types that ServiceStack should auto-wire by overriding CreateServiceManager, e.g:

public class AppHost : AppHostBase
{
    public AppHost(): base("Hello ServiceStack!", typeof(ServicesFromDll1).Assembly) {}
    public override void Configure(Container container) {}
    
    //Alternative way to inject Service Resolver strategy
    protected override ServiceController CreateServiceController(
        params Assembly[] assembliesWithServices)
    {       
        return new ServiceController(this, () => assembliesWithServices.ToList().SelectMany(x =>x.GetTypes()));
    }
}

Encapsulating Services inside Plugins

One way of modularizing services is to encapsulate them inside Plugins which allows you to manually register services, custom routes, filters, content types, allow customization and anything else your module needs.

To illustrate this point, we'll show what a Basic Auth Feature example might look like:

public class BasicAuthFeature : IPlugin 
{
    public string HtmlRedirect { get; set; }   //User-defined configuration

    public void Register(IAppHost appHost)
    {
        //Register Services exposed by this module
        appHost.RegisterService<AuthService>("/auth", "/auth/{provider}");
        appHost.RegisterService<AssignRolesService>("/assignroles");
        appHost.RegisterService<UnAssignRolesService>("/unassignroles");

        //Load dependent plugins
        appHost.LoadPlugin(new SessionFeature());
    }
}

With everything encapsulated inside a plugin, your users can easily enable them in your AppHost with:

Plugins.Add(new BasicAuthFeature { HtmlRedirect = "~/login" });

"No touch" Host Configuration

To improve modularization and reuse the configuration logic for a ServiceStack AppHost can be split over multiple files as seen with World Validation's AppHost where all it's Auth registration is maintained inside Configure.Auth.cs:

public class ConfigureAuth : IConfigureAppHost
{
    public void Configure(IAppHost appHost)
    {
        var AppSettings = appHost.AppSettings;
        appHost.Plugins.Add(new AuthFeature(() => new CustomUserSession(),
            new IAuthProvider[] {
            new CredentialsAuthProvider(), //Enable UserName/Password Credentials Auth
        }));

        appHost.Plugins.Add(new RegistrationFeature()); //Enable /register Service

        //override the default registration validation with your own custom implementation
        appHost.RegisterAs<CustomRegistrationValidator, IValidator<Register>>();

        appHost.Register<ICacheClient>(new MemoryCacheClient()); //Store User Sessions in Memory

        appHost.Register<IAuthRepository>(new InMemoryAuthRepository()); //Store Authenticated Users in Memory
   }
}

This modular approach makes it easy to "layer on" functionality with tools like web +.

ConfigureAppHost Interfaces

You can use this to refactor out different cohesive parts your Host configuration over multiple files and decouple them from your concrete AppHost which ServiceStack automatically runs all IPreConfigureAppHost, IConfigureAppHost and IPostConfigureAppHost interfaces on Startup it can find in either your AppHost Assembly or Service Assemblies specified in your AppHost constructor.

This opens up a number of re-use benefits where you'll be able to use the same AppHost configuration if your Services are being hosted in different Hosting Options, it makes it easy to maintain a standardized configuration across many of your ServiceStack projects, e.g. you can easily replace Configure.Auth.cs in all your projects to ensure they're running the same Auth Configuration without impacting any of the projects other bespoke host configuration.

Bundle Startup Logic in your Services Assembly

It also allows you to maintain any necessary Startup configuration that your Services implementation needs alongside the Services themselves.

E.g. This is used to register the Data.Contact to DTO Contact Auto Mapping:

// Register Custom Auto Mapping for converting Contact Data Model to Contact DTO
public class ContactsHostConfig : IConfigureAppHost 
{
    public void Configure(IAppHost appHost) =>
        AutoMapping.RegisterConverter((Data.Contact from) => from.ConvertTo<Contact>(skipConverters:true));
}

There are 3 different Startup interfaces you can use depending on when you want your configuration to run.

Register additional Service Assemblies on Startup

Use IPreConfigureAppHost for Startup logic you want to run before the AppHost starts initialization, this is run before AppHost.Config is initialized or Services are registered so has limited configurability but is useful if you want to register additional Service Assemblies with ServiceStack, e.g:

public class ConfigureContactsServices : IPreConfigureAppHost
{
    public void PreConfigure(IAppHost host) => host.ServiceAssemblies.AddIfNotExists(typeof(MyServices).Assembly);
}

Register no-touch Startup before and after your AppHost's Configure

Use IConfigureAppHost for Startup logic you want to run immediately before AppHost.Configure():

public interface IConfigureAppHost
{
    void Configure(IAppHost appHost);
}

Use Priority <= -1 for Startup logic you want to run before AppHost.Configure() and Priority >= 1 for logic you want to run immediately after AppHost.Configure():

[Priority(-1)]
public class MyPreAppHostConfigure : IConfigureAppHost
{
    public void Configure(IAppHost host) => ...;
}

[Priority(1)]
public class MyPostAppHostConfigure : IConfigureAppHost
{
    public void Configure(IAppHost host) => ...;
}