For those who just want code: https://github.com/SuavePirate/Xamarin.Onion
- Part 1 on the general project structure: Onionizing Xamarin Part 1
- Part 2 on our Domain and Application layers: Onionizing Xamarin Part 2
- Part 3 on our Infrastructure layer: Onionizing Xamarin Part 3
- Part 4 on our Client layer and Xamarin.Forms implementation: Onionizing Xamarin Part 4
- 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
- Why all these things are important
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.
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:
public class MockGenericRepository : IGenericRepository
private List _data;
_data = new List();
public void Add(T entity)
public void AddRange(IEnumerable 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();
public Task<IEnumerable> GetAsync(Func<T, bool> predicate)
var entities =_data?.Where(predicate);
public void Remove(T entity)
public class MockUserRepository : MockGenericRepository, IUserRepository
Now that we have some mock implementations, we can set up our tests against our Business logic.
public class UserBusinessTest
private IUserService _userService;
public void StartUpIoC ()
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
_userService = SimpleIoC.Default.GetInstance();
public async void AddUserTest()
var result = await _userService.CreateUserAsync(new NewUser
Email = "email@example.com",
FullName = "Testy McTest"
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.
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”.