Onionizing Xamarin Part 6

For those who just want code: https://github.com/SuavePirate/Xamarin.Onion 

Don’t forget:

  1. Part 1 on the general project structure: Onionizing Xamarin Part 1
  2. Part 2 on our Domain and Application layers: Onionizing Xamarin Part 2
  3. Part 3 on our Infrastructure layer: Onionizing Xamarin Part 3
  4. Part 4 on our Client layer and Xamarin.Forms implementation: Onionizing Xamarin Part 4
  5. Part 5 on creating custom Platform specific logic: Onionizing Xamarin Part 5

A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.

Some of the important topics this will cover:

  • Separation of Concerns
  • Inversion of Control
  • Dependency Injection
  • Model-View-ViewModel
  • Testability
  • Why all these things are important

Part 6

In this section, we will talk briefly about building useful tests for our solution, and why the Onion pattern makes it easy to break tests out into individual layers.

In this example, we will add a test project whose purpose it to just test the Business layer within our Infrastructure.

Tests.Business

Let’s start with by adding a nUnit project to our solution, or by adding the nuget package to a class library. Xamarin has great documentation on this: https://developer.xamarin.com/guides/cross-platform/application_fundamentals/installing-nunit-using-nuget/

In our project, we also want to install MvvmLight, just like in our Client and Platform layers. We will also need to add references to our Domain.Models, Domain.Interfaces, Application.Models, Application.Interfaces, and Infrastructure.Business projects.

In order to test our Infrastructure.Business project, we will need to create mock versions of our Data project. In our test project, we can create Repository implementations with mock data for each set that we need. For example:

MockGenericRepository.cs

public class MockGenericRepository : IGenericRepository
{
    private List _data;
    public MockGenericRepository()
    {
        _data = new List();
    }

    public void Add(T entity)
    {
        _data.Add(entity);
    }

    public void AddRange(IEnumerable entities)
    {
        _data.AddRange(entities);
    }

    public Task CommitAsync()
    {
        return Task.FromResult(false); // we don't need to explicitly save changes
    }

    public Task FindAsync(Func<T, bool> predicate)
    {
        var entity =_data.Where(predicate).FirstOrDefault();
        return Task.FromResult(entity);
    }

    public Task<IEnumerable> GetAsync(Func<T, bool> predicate)
    {
        var entities =_data?.Where(predicate);
        return Task.FromResult(entities);
    }

    public void Remove(T entity)
    {
        _data.Remove(entity);
    }
}

and MockUserRepository.cs

public class MockUserRepository : MockGenericRepository, IUserRepository
{
    public MockUserRepository()
    : base()
    {
    }
}

Now that we have some mock implementations, we can set up our tests against our Business logic.

UserBusinessTests.cs

public class UserBusinessTest
{
    private IUserService _userService;

    [SetUp]
    public void StartUpIoC ()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
        SimpleIoC.Default.Register<IUserService, UserService>();
        SimpleIoC.Default.Register<IUserRepository, MockUserRepository>();

        _userService = SimpleIoC.Default.GetInstance();
    }

    [Test ()]
    public async void AddUserTest()
    {
        var result = await _userService.CreateUserAsync(new NewUser
            {
                Email = "test@test.com",
                FullName = "Testy McTest"
            });
        Assert.IsNotNull(result.Data);
    }
}

Now we can test against any of the business logic in our application with a mock layer. The same practice can be applied to test any other layer in the solution as well. The data layer can be tested by mocking the business layer, and so on.

Conclusion

Looking back at all of the components of our Onion Architecture, one might think, “Wow, that’s a lot of code to do a simple task”. It’s important to remember that this architecture is not for every project. It’s focus is on scalability and testability. If your project has the potential to grow into something quite complicated, with many developers involved, this type of solution might work best for you. However, if you’re working on something quick to get out the door, maybe getting right to the point is easier and best for you.

The best parts about the Onion Architecture are its abilities to make drastic changes to tools or services used, without having to rewrite anything but that components implementation as well as making it easy to test individual layers without affecting the others or using real data. It also allows for closer monitoring and management of the codebase; keeping people from making calls directly from one layer to another. The only thing you have to emphasize is, “Are you adding a reference to another project to get something done? If so, you might be doing it wrong”.

Advertisements

Onionizing Xamarin Part 5

For those who just want code: https://github.com/SuavePirate/Xamarin.Onion 

Don’t forget:

  1. Part 1 on the general project structure: Onionizing Xamarin Part 1
  2. Part 2 on our Domain and Application layers: Onionizing Xamarin Part 2
  3. Part 3 on our Infrastructure layer: Onionizing Xamarin Part 3
  4. Part 4 on our Client layer and Xamarin.Forms implementation: Onionizing Xamarin Part 4

A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.

Some of the important topics this will cover:

  • Separation of Concerns
  • Inversion of Control
  • Dependency Injection
  • Model-View-ViewModel
  • Testability
  • Why all these things are important

Part 5

In this section, we will look at how to expand our Inversion of Control container with platform specific code. Specifically, we will implement some pieces of the HockeyApp SDK so that we can make calls to it from our Client or Infrastructure layers.

Our example will focus on just Android, but the same principles can be applied to any of the unique platform projects.

Platforms.Android

First thing we need to do is make sure we also install the MvvmLight nuget package in your Android project, as well as the HockeyApp Xamarin package.

From here, we can go back to our Application.Interface layer and create a new service:

ICrashAnalyticsService.cs

public interface ICrashAnalyticsService
{
    void Initialize();
    void GetFeedback();
}

Setting it up generic like this allows us to switch providers from HockeyApp to some other service should that be a need in the future.

Back in our Android project, let’s implement the ICrashAnalyticsServicewith our HockeyApp logic.

HockeyAppService.cs

public class HockeyAppService : ICrashAnalyticsService
{
    private const string HOCKEYAPP_KEY = "YOUR_HOCKEYAPP_KEY";
    private readonly Android.App.Application _androidApp;
    private readonly Activity _context;
    public HockeyAppService(Activity context, Android.App.Application androidApp)
    {
        _context = context;
        _androidApp = androidApp;
    }
    public void GetFeedback()
    {
        FeedbackManager.ShowFeedbackActivity(_context.ApplicationContext);
    }

    public void Initialize()
    {
        CrashManager.Register(_context, HOCKEYAPP_KEY);
        MetricsManager.Register(_androidApp, HOCKEYAPP_KEY);
        UpdateManager.Register(_context, HOCKEYAPP_KEY);
        FeedbackManager.Register(_context, HOCKEYAPP_KEY);
    }
}

Now we can create an IoCConfig class specific to our Android project. Because SimpleIoC uses a singleton for its container, we can register classes in our platform specific classes before our registrations in the Client layer.

AndroidIoCConfig.cs

public class AndroidIoCConfig
{
    public void RegisterAndroidServices(Android.App.Application application, Activity activity)
    {
        var hockeyService = new HockeyAppService(activity, application);
        hockeyService.Initialize();
        SimpleIoc.Default.Register<ICrashAnalyticsService>(() => hockeyService);
    }
}

Don’t forget to add a reference to the Application.Interfaces project in your platform project.

Lastly, let’s update our MainActivity to initialize our AndroidIoCConfig before we start up the  Xamarin.Forms app:

MainActivity.cs

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(bundle);
        global::Xamarin.Forms.Forms.Init(this, bundle);
        InitializeIoC();
        LoadApplication(new App());
    }

    private void InitializeIoC()
    {
        var container = new AndroidIoCConfig();
        container.RegisterAndroidServices(Application, this);
    }
}

Now we can make calls to our ICrashAnalyticsService from the Client layer, and use the Android specific logic. For example, we can pass the ICrashAnalyticsService into the constructor of a ViewModel, and call the GetFeedback() method to get access to the HockeyApp Feedback view.

ExampleViewModel.cs

public class ExampleViewModel : BasePageViewModel
{
    private readonly ICrashAnalyticsService _crashAnalyticsService;
    private ICommand _feedbackCommand;

    public ICommand FeedbackCommand
    {
        get
        {
            return _feedbackCommand ??
                (_feedbackCommand = new RelayCommand(() =>
                {
                    _crashAnalyticsService.GetFeedback();
                }));
        }
    }
}

It’s all that simple! The same pattern can be applied to anything that needs to be platform specific.

What’s Next

In the next and final segment, we will look at building mock implementation of our Infrastructure layer and using them to test layers individually in Unit tests.

Next: Onionizing Xamarin Part 6

Onionizing Xamarin Part 4

For those who just want code: https://github.com/SuavePirate/Xamarin.Onion 

Don’t forget:

  1. Part 1 on the general project structure: Onionizing Xamarin Part 1
  2. Part 2 on our Domain and Application layers: Onionizing Xamarin Part 2
  3. Part 3 on our Infrastructure layer: Onionizing Xamarin Part 3

A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.

Some of the important topics this will cover:

  • Separation of Concerns
  • Inversion of Control
  • Dependency Injection
  • Model-View-ViewModel
  • Testability
  • Why all these things are important

Part 4

In this section, we will look at the code for our actual Xamarin.Forms client implementation along with talking about building other Non-Xamarin clients into our solution, and sharing as much code between them as possible. This are our Client layer, and will include setting up our Views, ViewModels, IoC container, and start up process.

Client Layer

First thing is first, let’s build our ViewModels. These ViewModels are going to interface with our Application layer by making calls to our defined Service Interfaces that will be injected into the constructors of our ViewModels.

Some things to note: We are using MVVM Light in this example to make our MVVM and IoC easier to get going. So things such as the ViewModelBase class and the Set() method are coming from that library. You can choose to utilize a different library, or roll your own pretty easily. In either case, the principles are the same.

Let’s first abstract some universal properties into a BasePageViewModel.cs

public class BasePageViewModel : ViewModelBase
{
    private bool _isLoading;
    private bool _isEnabled;
    private string _title;
    private ObservableCollection<string> _errors;

    public bool IsLoading
    {
        get
        {
            return _isLoading;
        }
        set
        {
            Set(() => IsLoading, ref _isLoading, value);
        }
    }
    public bool IsEnabled
    {
        get
        {
            return _isEnabled;
        }
        set
        {
            Set(() => IsEnabled, ref _isEnabled, value);
        }
    }
    public ObservableCollection<string> Errors
    {
        get
        {
            return _errors;
        }
        set
        {
            Set(() => Errors, ref _errors, value);
        }
    }

    public string Title
    {
        get
        {
            return _title;
        }
        set
        {
            Set(() => Title, ref _title, value);
        }
    }
    public BasePageViewModel()
    {
        IsEnabled = true;
        IsLoading = false;
    }

    public override void Cleanup()
    {
        base.Cleanup();
        Errors = null;
    }
}

 

From here let’s make a ViewModel for each of our pages (in this example, we will just look at one “MainPage”)

MainPageViewModel.cs

public class MainPageViewModel : BaseViewModel
{
    private readonly IUserService _userService;
    private string _bodyTitle;
    private string _bodyText;
    public string BodyTitle
    {
        get
        {
            return _bodyTitle;
        }
        set
        {
            Set(() => BodyTitle, ref _bodyTitle, value);
        }
    }

    public string BodyText
    {
        get
        {
            return _bodyText;
        }
        set
        {
            Set(() => BodyText, ref _bodyText, value);
        }
    }

    private async void Initialize()
    {
        IsLoading = true;
        await Task.Delay(2000); // simulate load time
        var users = await _userService.GetValidUsers();
        if(users?.Data == null || users.Data.Count() == 0)
        {
            var user = await _userService.CreateUserAsync(new NewUser
            {
                FullName = "Felipe Fancybottom",
                Email = "feffancy@fancybottoms.com"
            });

            BodyText = user.Data.Email;
            BodyTitle = user.Data.FullName;
        }
        else
        {
            BodyText = users.Data.First().Email;
            BodyTitle = users.Data.First().FullName;
        }
        IsLoading = false;
    }

    public MainPageViewModel(IUserService userService)
    {
        _userService = userService;

        Title = "Onion Template";
        BodyTitle = "Loading Name";
        BodyText = "Loading Email";
        Initialize();
    }
}

Notice how we injected the IUserService in the constructor, and use that to lazy load some data into our bindable properties. When we create our view and set the BindingContext to this ViewModel, we will see the UI automatically update when those do. This example does it async right from the constructor, but you can load your data and set up your initial properties any way you’d like.

The next step is to initialize our Inversion of Control, Dependency Injection, and ViewModelLocator to tie all our layers together and allow us to automatically set the BindingContext of our Page.

If it makes sense to you, you can break the IoC set up into a separate project that references all the previous layers. For the sake of simplicity, we are going to do it in the same project as our Xamarin.Forms project.

IoCConfig.cs

public class IoCConfig
{
    public IoCConfig()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
    }

    public void RegisterViewModels()
    {
        SimpleIoc.Default.Register<MainViewModel>();
    }

    public void RegisterRepositories()
    {
        SimpleIoc.Default.Register<IUserRepository, UserRepository>(); // this is where you would change the registration to use a different repository
    }

    public void RegisterServices()
    {
        SimpleIoc.Default.Register<IUserService, UserService>();
    }

    public void RegisterProviders()
    {
        SimpleIoc.Default.Register<IUserDataProvider, UserDataProvider>();
        SimpleIoc.Default.Register<ICloudStorageProvider, AzureStorageDataProvider>(); // this is where you would change the registration to use a different provider
    }

    public void RegisterStores()
    {
        SimpleIoc.Default.Register<IUserStore, UserStore>();
        SimpleIoc.Default.Register<IStoreManager, StoreManager>();
    }
}

The purpose of this class is to wire up our dependencies as well as our actual container for the ServiceLocator. This example is using SimpleIoc which is packaged with MVVM Light.

Now that we have our other layers glued together, we just need to create our ViewModelLocator to automatically handle our bindings, then make calls to the IoCConfig when the ViewModelLocator is initialized.

ViewModelLocator.cs

public class ViewModelLocator
{
    public MainPageViewModel MainPage
    {
        get
        {
            return ServiceLocator.Current.GetInstance<MainPageViewModel >();
        }
    }
    public ViewModelLocator()
    {
        var iocConfig = new IoCConfig();
        iocConfig.RegisterRepositories();
        iocConfig.RegisterProviders();
        iocConfig.RegisterServices();
        iocConfig.RegisterViewModels();
        iocConfig.RegisterStores();
    }
}

In our constructor, we initialize our IoC, and also provide properties for each of our ViewModels, so that we can bind it easily in our XAML.

The last two steps here are to add a Resource in our App.xaml to our ViewModelLocator, and create our Page.

App.xaml


<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="OnionTemplate.App">
    <Application.Resources>
        <!-- Application resource dictionary -->
        <ResourceDictionary>
                <vm:ViewModelLocator xmlns:vm="clr-namespace:NAMESPACEOF.VIEWMODELLOCATOR;assembly=YOURPACKAGENAME" x:Key="Locator" />
        </ResourceDictionary>
    </Application.Resources>
</Application>

Now that we have our resource, let’s create our page and wire up the BindingContext in our XAML.

MainPage.xaml


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="NAMESPACE.MainPage" BindingContext="{Binding Source={StaticResource Locator}, Path=Main}" Title="{Binding Title}">
    <ContentPage.Content>
        <StackLayout Orientation="Vertical" HorizontalOptions="Center" VerticalOptions="Center">
            <Label Text="{Binding BodyTitle}"/>
            <Label Text="{Binding BodyText}"/>
            <ActivityIndicator IsRunning="True" IsVisible="{Binding IsLoading}"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

There is nothing required to write in our code behind (MainPage.xaml.cs) since it is all automatically wired up.

Last but not least, set our page in our App.xaml.cs:

App.xaml.cs


public partial class App : Xamarin.Forms.Application
{
    public App()
    {
        InitializeComponent();

        MainPage = new NavigationPage(new MainPage());
    }
}

At this point, we should be able to run the application (assuming that the Xamarin.Forms app is started off in each platform the way the template sets it up).

So we have our Xamarin.Forms implementation. But what about other applications that can’t use Xamarin.Forms? Web apps? Xamarin.Mac apps? Cloud apps? WPF?

Here is one of the coolest parts of the entire Onion pattern. We can go ahead and add more projects into our Client layer. These layers can use the same models, interfaces, and in some cases, implementations! For projects where we would need completely different logic, such as a Web App for example, we can implement multiple versions of the Domain and Application layers.

In our web app, we could create another project in the Infrastructure layer (say Infrastructure.WebData) that uses Entity Framework and SQL. Then in our IoCConfig of our Web App, we call to register our Infrastructure.WebData implementations for our Domain.Interfaces.

As long as each project in the Client layer serves the same purpose of configuring Views, and starting up our application with our Inversion of Control, any type of application can live here and follow the same pattern.

The Client layer can also contain abstractions of controls or other utilities that can be referenced by the core Client projects.

What’s Next

In the next segment, we will look at how to integrate our individual mobile platforms, and how to inject custom platform-specific code with some examples using the HockeyApp SDK.

 

Next: Onionizing Xamarin Part 5

Onionizing Xamarin Part 3

For those who just want code: https://github.com/SuavePirate/Xamarin.Onion 

Don’t forget:

  1. Part 1 on the general project structure: Onionizing Xamarin Part 1
  2. Part 2 on our Domain and Application layers: Onionizing Xamarin Part 2

A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.

Some of the important topics this will cover:

  • Separation of Concerns
  • Inversion of Control
  • Dependency Injection
  • Model-View-ViewModel
  • Testability
  • Why all these things are important

Part 3

In this section, we’ll start to dive into the code for our infrastructure layers (or at least what is important), including our business logic and data logic.

Let’s dive into the data layer.

Infrastructure.Data

This layer is our actual implementation of our Domain definitions, so we are going to implement things such as our Repositories, DataProviders, Stores, or anything else that interacts with our data directly.

From our previous post we defined our IGenericStore + IUserStore and our IGenericRepository + IUserRepository, so now let’s implement them.

GenericStore.cs and UserStore.cs

public class GenericStore<T> : IGenericStore<T>
{
    public List<T> Data { get; set; }
    public GenericStore()
    {
        Data = new List<T>();
    }
}

public class UserStore : GenericStore<User>, IUserStore
{
}

For the sake of just testing data, our store just contains a collection of data, however, this is where you could implement an observable collection, or more complex data types as well.

Now a look at the repositories – Our implementation of our repository is just going to use in-memory storage, but this is a place where you could implement SqlLite, Azure Mobile Tables, or local file storage instead. You could implement all of these easily and just switch out in your UserRepository which one it inherits! That’s one of the biggest bonuses of our Onion Architecture. The github repository demonstrates this well: https://github.com/SuavePirate/Xamarin.Onion/blob/master/src/OnionTemplate/OnionTemplate.Infrastructure.Data/Repositories/UserRepository.cs

GenericMemoryRepository.cs and UserRepository.cs

public class GenericMemoryRepository<T> : IGenericRepository<T>
{
    private readonly IStoreManager _storeManager;
    public GenericMemoryRepository(IStoreManager storeManager)
    {
        _storeManager = storeManager;
    }
    public void Add(T entity)
    {
        _storeManager.Set<T>().Data.Add(entity);
    }

    public void AddRange(IEnumerable<T> entities)
    {
        _storeManager.Set<T>().Data.AddRange(entities);
    }

    public Task CommitAsync()
    {
        return Task.FromResult(false); // we don't need to explicitly save changes
    }

    public Task<T> FindAsync(Func<T, bool> predicate)
    {
        var entity = _storeManager.Set<T>().Data.Where(predicate).FirstOrDefault();
    return Task.FromResult(entity);
    }

    public Task<IEnumerable<T>> GetAsync(Func<T, bool> predicate)
    {
        var entities = _storeManager.Set<T>()?.Data?.Where(predicate);
        return Task.FromResult(entities);
    }

    public void Remove(T entity)
    {
        _storeManager.Set<T>().Data.Remove(entity);
    }

    public void RemoveRange(T entities)
    {

    }
}

public class UserRepository : GenericMemoryRepository<User>, IUserRepository
{
    public UserRepository(IStoreManager manager)
    : base(manager)
    {
    }
}

That’s all we need to define for our data  layer for now. Next let’s look at our business logic layer and how it interacts with the data layer through references to our domain interfaces.

Infrastructure.Business

Our business layer is our implementation of our Application layer. So we are going to implement the IBaseService and IUserService we defined in the previous segment:

IBaseService.cs and IUserService.cs

public class BaseService : IBaseService
{
    public BaseService()
    {
    }

    public IEnumerable<string> Validate(object model)
    {
        if(model == null)
            return new List<string> { "Empty model received" };
        return null;
    }
}

public class UserService : BaseService, IUserService
{
    private readonly IUserRepository _userRepository;
    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }
    public async Task<Result<UserTransferObject>> CreateUserAsync(NewUser model)
    {
        var errors = Validate(model);
        if (errors == null)
        {
            var entity = model.ToUser();
            _userRepository.Add(entity);
            await _userRepository.CommitAsync();

            return new Result<UserTransferObject>(new UserTransferObject(entity));
        }
        return new Result<UserTransferObject>(ResultType.Invalid, errors);
    }

    public async Task<Result<UserTransferObject>> FindByIdAsync(int userId)
    {
        var entity = await _userRepository.FindAsync(user => user.Id == userId);
        if (entity == null)
        {
            return new Result<UserTransferObject>(ResultType.Failed, "Could not find user with this Id");
        }
        return new Result<UserTransferObject>(new UserTransferObject(entity));
    }

    public async Task<Result<UserTransferObject>> RemoveByIdAsync(int userId)
    {
        var entity = await _userRepository.FindAsync(user => user?.Id == userId);
        if (entity == null)
        {
            return new Result<UserTransferObject>(ResultType.Failed, "Could not find user with this Id");
        }
        _userRepository.Remove(entity);
        await _userRepository.CommitAsync();
        return new Result<UserTransferObject>(new UserTransferObject(entity));
    }

    public async Task<Result<IEnumerable<UserTransferObject>>> GetValidUsers()
    {
        var entities = await _userRepository.GetAsync(user =>         !string.IsNullOrEmpty(user?.Email));
        return new Result<IEnumerable<UserTransferObject>>(entities?.Select(entity => new UserTransferObject(entity)));
    }
}

The biggest thing to point out is how the constructor for our UserService takes in an IUserRepository. Later we will set up our IoC container and inject our actual UserService so that the logic ties together. Doing this allows us to avoid referencing the Infrastructure.Data layer in our Infrastructure.Business layer which gives us full Separation of Concerns in our layers.

What’s Next

In the next segment, we’ll talk about implementing our Xamarin.Forms application, setting up our Inversion of Control and Dependency Injection, and tying it all together.

We’ll also look at each of our different platforms and talk about how we can utilize them without using Xamarin.Forms.

Finally, in the last segment, we will talk about how to truly utilize the Onion Architecture to test, pull, and change important pieces of our application without having to touch anything else.

 

Check out Part 4 to look at the Client Layer

Onionizing Xamarin Part 2

For those who just want code: https://github.com/SuavePirate/Xamarin.Onion 

Don’t forget part 1 on the general project structure: Onionizing Xamarin Part 1

A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.

Some of the important topics this will cover:

  • Separation of Concerns
  • Inversion of Control
  • Dependency Injection
  • Model-View-ViewModel
  • Testability
  • Why all these things are important

Part 2

In this section, we’ll start to dive into the code for our definition layers (or at least what is important).

Let’s get into the Domain and Application layers:

Domain.Models

As said before, this is where our core models are, so let’s take one as an example:

User.cs

 public class User
 {
     public int Id { get; set; }
     public string Email { get; set; }
     public string FullName { get; set; }
     public string PasswordHash { get; set; }
 }

We’ll focus on our one model, but you could grow your entities out here.

Domain.Interfaces

This is where we define our data access layer for consuming our Domain.Models; Stores, DataProviders, and Repositories. These are good places to set up generic definitions so that multiple implementations can be made more easily. Here are examples of each:

IGenericStore.cs and IUserStore.cs

 public interface IGenericStore<T>
 {
     List<T> Data { get; set; }
 }

 public interface IUserStore : IGenericStore<User>
 {
 }

If you were to need to define custom methods for the user, you can do that in your IUserStore

Another common practice is to add a manager wrapper for your stores, to make it easier to inject the use of multiple stores in our business layer in the future. In this example, it would look something like this:
IStoreManager.cs

public interface IStoreManager
{
    IUserStore UserStore { get; }
    IGenericStore<T> Set<T>();
}

IGenericRepository.cs and IUserRepository.cs


 public interface IGenericRepository<T>
 {
     void Add(T entity);
     void AddRange(IEnumerable&lt;T&gt; entities);
     void Remove(T entity);
     void RemoveRange(T entities);
     Task<T> FindAsync(Func<T, bool> predicate);
     Task<IEnumerable<T>> GetAsync(Func<T, bool> predicate);
     Task CommitAsync();
 }
 public interface IUserRepository : IGenericRepository<User>
 {
 }

Just as with the Stores, you can define entity specific methods / queries in your specific repository (IUserRepository).

Application.Models

Now that we are through our data definition layers, let’s take a look at the application definition layers, starting with Application.Models. This is where our business models live – our Data Transfer Object Models, Input Models, Output Models, etc. So here is how our Domain.Models.User maps to each of these types:

UserTransferObject.cs


 public class UserTransferObject
 {
     public int Id { get; set; }
     public string Email { get; set; }
     public string FullName { get; set; }
     public UserTransferObject()
     {
     }
     public UserTransferObject(User entity)
     {
         Id = entity.Id;
         Email = entity.Email;
         FullName = entity.FullName;
     }
 }

Note that we have added a constructor that also consumes a Domain.Models.User type. This is completely optional. Many people do not want the Application.Models layer to reference any other layer. Another common way to handle the mapping is via an extension class in the Infrastructure.Business layer, like so:


public static class UserExtensions
{
     public static UserTransferObject ToDTO(this User entity)
     {
         return new UserTransferObject
         {
            Id = entity.Id;
            Email = entity.Email;
            FullName = entity.FullName;
         }
     }
}

The important thing to note in all of this, is that the DTO has properties mapped from the entity that are relevant and SAFE to the application. Notice the PasswordHash field was omitted.

NewUser.cs


 public class NewUser
 {
     public string Email { get; set; }
     public string FullName { get; set; }
     public string NewPassword { get; set; }
 }

This is one of our input models for creating a new User. Notice that it only has the properties required for creating one.

Last but not least, our output. This example uses a generic output Result that holds data from a DTO, errors, and the type of result. The output models you use will depend on the services you’re using, so this is not a catch-all.

Result.cs and ResultType.cs


 public class Result<T>
 {
     public ResultType Type { get; set; }
     public IEnumerable<string> Errors { get; set; }
     public T Data { get; set; }

     public Result(T data)
     {
         Data = data;
         Type = ResultType.Ok;
         Errors = new List<string>();
     }

     public Result(ResultType type, IEnumerable<string> errors)
     {
         Type = type;
         Errors = errors;
     }

     public Result(ResultType type, string error)
     {
         Type = type;
         Errors = new List<string> { error };
     }
 }

 public enum ResultType
 {
     Ok,
     BadRequest,
     Failed,
     Unauthorized,
     Forbidden,
     Invalid
 }

Now we have our models, let’s define our business layer.

Application.Interfaces

These are the definitions of our business logic that use our Application.Model layer. We’ll use services here, and like our data definitions, will utilize generic definitions where possible.

IBaseService.cs and IUserService.cs


 public interface IBaseService
 {
     IEnumerable<string> Validate(object model);
 }

 public interface IUserService : IBaseService
 {
    Task<Result<UserTransferObject>> CreateUserAsync(NewUser model);
    Task<Result<UserTransferObject>> FindByIdAsync(int userId);
    Task<Result<UserTransferObject>> RemoveByIdAsync(int userId);
    Task<Result<IEnumerable<UserTransferObject>>> GetValidUsers();
 }

Notice that each of our consumes either a primitive type, or an input model from our Application.Models and outputs one of our output models.

What’s Next

That’s all there is for the different definition layers. In the next post, we’ll look at implementing these two layers in our Infrastructure.Data and Infrastructure.Business layers. From there, we can look at our actual Xamarin code for consuming these layers and mapping them all together.

Once we’ve gone over all our layers, we will look into replacing different pieces, building tests, and areas where you can add your own flare.

Check out Part 3 to look at those Infrastructure layers.

Onionizing Xamarin Part 1

For those who just want code: https://github.com/SuavePirate/Xamarin.Onion 

A strong and scale-able architecture is important in applications, especially in Mobile Apps. APIs and SDKs are constantly changing, new technology is constantly released, and team sizes are always changing. A solid Onion Architecture can save a development team a lot of time by making it simple to change service implementations, restrict access to certain areas, making logic flow easy to follow, and making testing isolated blocks of code easier.

Some of the important topics this will cover:

  • Separation of Concerns
  • Inversion of Control
  • Dependency Injection
  • Model-View-ViewModel
  • Testability
  • Why all these things are important

Part 1

This first post will talk about the general project structure and high level role of each layer in the solution. Later posts will touch on the individual projects’ code, why things are where they are, using the structure to build out tests, and ways to bend or change the structure to work for you.

Project Structure

For this example we will be talking about Xamarin.Forms, but the same patterns can be applied in the exact same way without it.
Lets just take a look at the high level structure and layers of the solution:

xamarinonionsolution

Domain

This is the lowest level. Projects within Domain should not reference any projects outside of this layer and should also avoid referencing any external libraries.

Domain.Models

These are our base data models. This project shouldn’t reference any other projects, even within Domain.

Domain.Interfaces

These are the definitions for our data access layer. Repositories, Stores, etc. There should be no implementation in this project and should only reference the Model project.

Application

This layer is what defines our Services and Business logic without implementing any of it. Projects in this layer can only reference Model layers of Domain.

Application.Models

These are our application models such as input models, data transfer objects, as well as any helpers for mapping Domain models to these. This project will only reference the Domain.Models to help map them to DTOs or other models. This, however, is also optional. You could opt to handle mapping externally when the models are needed in business logic rather than in the Application layer.

Application.Interfaces

These are the definitions for our business logic layer. Services, Managers, etc. There should be no implementation in this project and it should only reference the Application.Models project

Infrastructure

This is where we implement our data and business logic.

Infrastructure.Data

This is the implementation of our Data access layer. Communicate with 3rd party data providers, local storage, databases, etc. Domain.Interfaces should be implemented here.

Infrastructure.Business

This is the implementation of our Business logic layer. Communicate with the data layer through contracts from the Domain.Interfaces. This project should NOT reference the Data project. Application.Interfaces should be implemented here

Clients

This is where we implement client logic, set up IoC, create ViewModels, and controls. If we are using Xamarin.Forms, this is where the PCL or Shared Library with Xamarin.Forms is. This Layer can reference any of the above layers in order to wire up IoC. If you have multiple Xamarin.Forms projects in one solution, they can both be here.

Clients.Controls

This is where reusable controls within Xamarin.Forms exist. Pretty straightforward.

Platforms

This is the section where the Native Projects live. If you have too many native projects for things like wearables, or the different TV OS’s, then it might make sense to break this section into smaller sections for things like “Apple”, “Google”, “Windows”, or something similar. But for the sake of this demo, we are only working with one project for each platform, so they live together.

This layer should only reference the Client layer and Binding Layer

Droid

This is the Xamarin.Android project. If any native services need to be called from one of the shared layers, the IoC set up can be extended into this project and one of the interfaces could be implemented here and registered.

iOS

This is the Xamarin.iOS project. As stated above, native services can be set up and injected from here as well.

UWP

This is the UWP project. As stated above, native services can be set up and injected from here as well.

Binding

This is where Xamarin Binding projects should be if there is a need for binding to any native libraries. As with the Platforms layer, if there are many different binding projects, this layer could be split into different sections for each OS. This layer should be exclusive and not reference any other layer.

Tests

This is where UI and Unit tests are. This layer can reference any other level in order to test them. This layer can wire up a completely different IoC container, contain mock projects for simulating any other layer, or any other external reference.

 

Continue on:

Onionizing Xamarin Part 2