// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Build the WRITE side (we use a bus for it) ---------- var bus = new FakeBus(); var bookingRepository = new BookingAndClientsRepository(); var bookingHandler = CompositionRootHelper.BuildTheWriteModelHexagon(bookingRepository, bookingRepository, bus, bus); // TODO: register handlers for the query part coming from the bus? // Build the READ side --------------------------------- var hotelsAdapter = new HotelsAndRoomsAdapter($"{env.WebRootPath}/hotels/", bus); hotelsAdapter.LoadAllHotelsFiles(); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter, null, bus); // Registers all services to the MVC IoC framework services.AddSingleton <ISendCommands>(bus); services.AddSingleton <IQueryBookingOptions>(readFacade); services.AddSingleton <IProvideHotel>(readFacade); services.AddSingleton <IProvideReservations>(readFacade); // Add framework services. services.AddMvc(); }
public void Debe_actualizar_pedidoEngine_cuando_cancela_pedidoCommand_es_enviado() { var pedidoEngine = new PedidoYClientesRepository(); var bus = new FakeBus(); CompositionRootHelper.BuildTheWriteModelHexagon(pedidoEngine, pedidoEngine, bus, bus); var pizzeriaId = 1; var cantidad = 1; var clienteId = "*****@*****.**"; var pedidoCommand = new PedidoCommand( clienteId: clienteId, pizzaNombre: "Malcriada", pizzeriaId: pizzeriaId, cantidad: cantidad, fechaDeEntrega: Constants.MyFavoriteSaturdayIn2019); bus.Send(pedidoCommand); Check.That(pedidoEngine.GetPedidoDe(clienteId)).HasSize(1); var deliveryGuid = pedidoEngine.GetPedidoDe(clienteId).First().PedidoId; var cancelDeliveryCommand = new CancelarPedidoCommand(deliveryGuid, clienteId); bus.Send(cancelDeliveryCommand); // Pedido is still there, but canceled Check.That(pedidoEngine.GetPedidoDe(clienteId)).HasSize(1); Check.That(pedidoEngine.GetPedidoDe(clienteId).First().EsCancelado).IsTrue(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure <CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); // Build the WRITE side (we use a bus for it) ---------- var bus = new FakeBus(); var bookingRepository = new BookingAndClientsRepository(); var bookingHandler = CompositionRootHelper.BuildTheWriteModelHexagon(bookingRepository, bookingRepository, bus, bus); // TODO: register handlers for the query part coming from the bus? // Build the READ side --------------------------------- var hotelsAdapter = new HotelsAndRoomsAdapter($"{env.WebRootPath}/hotels/", bus); hotelsAdapter.LoadAllHotelsFiles(); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter, null, bus); // Registers all services to the MVC IoC framework services.AddSingleton <ISendCommands>(bus); services.AddSingleton <IQueryBookingOptions>(readFacade); services.AddSingleton <IProvideHotel>(readFacade); services.AddSingleton <IProvideReservations>(readFacade); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
public static void RegisterGlobalFilters(GlobalFilterCollection filters, IKernel kernel) { filters.Add(new CustomHandleErrorAttribute(kernel.Get <ILog>())); filters.Add(new CultureFilterAttribute(CompositionRootHelper.GetLanguage(kernel))); filters.Add(new AccessAuthorizationFilter(() => kernel.Get <IIdentityService>())); }
public void Should_find_no_room_when_searching_an_empty_location_catalog() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); var searchQuery = new SearchBookingOptions(checkInDate: DateTime.Now, checkOutDate: DateTime.Now.AddDays(1), location: "Paris", numberOfAdults: 2, numberOfRoomsNeeded: 1, childrenCount: 0); var bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).IsNotNull().And.IsEmpty(); }
public void Should_throw_exception_when_checkinDate_is_after_checkOutDate() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); Check.ThatCode(() => { var searchQuery = new SearchBookingOptions(checkInDate: DateTime.Now.AddDays(1), checkOutDate: DateTime.Now, location: "Kunming", numberOfAdults: 1); return(readFacade.SearchBookingOptions(searchQuery)); }) .Throws <InvalidOperationException>(); }
public void Should_find_hotels_despite_incorrect_case_for_location() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); hotelsAdapter.LoadHotelFile("New York Sofitel-availabilities.json"); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); var searchedLocation = "new york"; var searchQuery = new SearchBookingOptions(Constants.MyFavoriteSaturdayIn2017, checkOutDate: Constants.MyFavoriteSaturdayIn2017.AddDays(1), location: searchedLocation, numberOfAdults: 2, numberOfRoomsNeeded: 1, childrenCount: 0); var bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).HasSize(1); }
public void Should_stop_sending_Orders_to_a_Market_after_3_rejects() { var rejectingMarket = new ApiMarketGateway("LSE (London)", sellQuantity: 100, sellPrice: 100M, orderPredicate: order => false); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(rejectingMarket); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 50, price: 100M, goodTill: DateTime.Now.AddMinutes(5)); investorInstructionAdapter.Route(investorInstructionDto, (args) => { }, (args) => { }); Check.That(rejectingMarket.TimesSent).IsEqualTo(3); Check.That(rejectingMarket.SellQuantity).IsEqualTo(100); }
public void Should_find_only_hotels_that_match_location_and_availability_for_this_period() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); hotelsAdapter.LoadHotelFile("THE GRAND BUDAPEST HOTEL-availabilities.json"); // available hotelsAdapter.LoadHotelFile("Danubius Health Spa Resort Helia-availabilities.json"); // available hotelsAdapter.LoadHotelFile("BudaFull-the-always-unavailable-hotel-availabilities.json"); // unavailable var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); var searchQuery = new SearchBookingOptions(Constants.MyFavoriteSaturdayIn2017, checkOutDate: Constants.MyFavoriteSaturdayIn2017.AddDays(1), location: "Budapest", numberOfAdults: 2, numberOfRoomsNeeded: 1, childrenCount: 0); var bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).HasSize(2); }
public void Should_impact_booking_repository_when_sending_a_booking_command() { var bus = new FakeBus(synchronousPublication: true); var bookingRepository = new Mock <ISaveBooking>(); var clientRepository = new Mock <IHandleClients>(); CompositionRootHelper.BuildTheWriteModelHexagon(bookingRepository.Object, clientRepository.Object, bus, bus); bookingRepository.Verify(x => x.Save(It.IsAny <Booking>()), Times.Never); var bookingCommand = new BookingCommand(clientId: "*****@*****.**", hotelName: "Super Hotel", hotelId: 1, roomNumber: "2", checkInDate: DateTime.Parse("2016-09-17"), checkOutDate: DateTime.Parse("2016-09-18")); bus.Send(bookingCommand); bookingRepository.Verify(x => x.Save(It.Is <Booking>(y => y.ClientId == "*****@*****.**")), Times.Once); }
public void Should_get_hotel_from_its_id() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); hotelsAdapter.LoadHotelFile("New York Sofitel-availabilities.json"); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); var hotelId = 1; var hotel = readFacade.GetHotel(hotelId: hotelId); Check.That(hotel.Identifier).IsEqualTo(hotelId); Check.That(hotel.Name).IsEqualTo("New York Sofitel"); Check.That(hotel.Location).IsEqualTo("New York"); Check.That(hotel.NumberOfRooms).IsEqualTo(405); }
public void Debe_actualizar_buscar_pizza_cuando_cancelaPedidoCommand_es_enviado() { // Initialize Read-model side var bus = new FakeBus(synchronousPublication: true); var pizzeriasAdapter = new PizzeriasYPizzasAdapter(Constants.RelativePathForPizzaIntegrationFiles, bus); var pedidoAdapter = new ReservaAdapter(bus); pizzeriasAdapter.LoadPizzaFile("Malcriada-availabilities.json"); // Initialize Write-model side var bookingRepository = new PedidoYClientesRepository(); CompositionRootHelper.BuildTheWriteModelHexagon(bookingRepository, bookingRepository, bus, bus); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(pizzeriasAdapter, pizzeriasAdapter, pedidoAdapter, bus); // Search Pizzas availabilities var fechaEntrega = Constants.MyFavoriteSaturdayIn2019; var searchQuery = new BuscarPedidoOpciones(fechaEntrega, direccion: "Cercado", nombrePizza: "peperoni", cantidad: 2); var pedidoOpciones = readFacade.BuscarPedidoOpciones(searchQuery); // We should get 1 pedido option with 13 available pizzas in it. Check.That(pedidoOpciones).HasSize(1); var pedidoOpcion = pedidoOpciones.First(); var initialPizzaNumbers = 13; Check.That(pedidoOpcion.DisponiblesPizzasConPrecios).HasSize(initialPizzaNumbers); // Now, let's request that pizza! var firstPizzaOfThisPedidoOption = pedidoOpcion.DisponiblesPizzasConPrecios.First(); var clientId = "*****@*****.**"; var pedidoCommand = new PedidoCommand(clienteId: clientId, pizzeriaId: pedidoOpcion.Pizzeria.Identificador, pizzaNombre: "MalcriadaC", cantidad: firstPizzaOfThisPedidoOption.Cantidad, fechaDeEntrega: fechaEntrega); // We send the PedirPizza command bus.Send(pedidoCommand); // We check that both the PedidoRepository (Write model) and the available pizzas (Read model) have been updated. Check.That(bookingRepository.GetPedidoDe(clientId).Count()).IsEqualTo(1); var pedidoId = bookingRepository.GetPedidoDe(clientId).First().PedidoId; // Fetch pizzas availabilities now. One pizza should have disappeared from the search result pedidoOpciones = readFacade.BuscarPedidoOpciones(searchQuery); Check.That(pedidoOpciones).HasSize(1); Check.That(pedidoOpcion.DisponiblesPizzasConPrecios).As("available matching pizzas").HasSize(initialPizzaNumbers - 1); }
public void Should_impact_both_write_and_read_models_when_sending_a_booking_command() { // Initialize Read-model side var bus = new FakeBus(synchronousPublication: true); var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, bus); var reservationsAdapter = new ReservationAdapter(bus); hotelsAdapter.LoadHotelFile("New York Sofitel-availabilities.json"); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter, reservationsAdapter, bus); // Search Rooms availabilities var checkInDate = Constants.MyFavoriteSaturdayIn2017; var checkOutDate = checkInDate.AddDays(1); var searchQuery = new SearchBookingOptions(checkInDate, checkOutDate, location: "New York", numberOfAdults: 2); var bookingOptions = readFacade.SearchBookingOptions(searchQuery); // We should get 1 booking option with 13 available rooms in it. Check.That(bookingOptions).HasSize(1); var bookingOption = bookingOptions.First(); var initialRoomsNumbers = 13; Check.That(bookingOption.AvailableRoomsWithPrices).HasSize(initialRoomsNumbers); // Now, let's book that room! var firstRoomOfThisBookingOption = bookingOption.AvailableRoomsWithPrices.First(); var bookingCommand = new BookingCommand(clientId: "*****@*****.**", hotelName: "New York Sofitel", hotelId: bookingOption.Hotel.Identifier, roomNumber: firstRoomOfThisBookingOption.RoomIdentifier, checkInDate: checkInDate, checkOutDate: checkOutDate); // Initialize Write-model side var bookingRepository = new BookingAndClientsRepository(); CompositionRootHelper.BuildTheWriteModelHexagon(bookingRepository, bookingRepository, bus, bus); // We send the BookARoom command bus.Send(bookingCommand); // We check that both the BookingRepository (Write model) and the available rooms (Read model) have been updated. Check.That(bookingRepository.GetBookingsFrom("*****@*****.**").Count()).IsEqualTo(1); // Fetch rooms availabilities now. One room should have disappeared from the search result bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).HasSize(1); Check.That(bookingOption.AvailableRoomsWithPrices).As("available matching rooms").HasSize(initialRoomsNumbers - 1); }
public void Should_find_one_more_matching_hotel_after_new_hotel_is_integrated() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); // Integrates a first hotel hotelsAdapter.LoadHotelFile("THE GRAND BUDAPEST HOTEL-availabilities.json"); var searchQuery = new SearchBookingOptions(Constants.MyFavoriteSaturdayIn2017, checkOutDate: Constants.MyFavoriteSaturdayIn2017.AddDays(1), location: "Budapest", numberOfAdults: 2, numberOfRoomsNeeded: 1, childrenCount: 0); var bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).HasSize(1); // Loads a new hotel that has available room matching our research hotelsAdapter.LoadHotelFile("Danubius Health Spa Resort Helia-availabilities.json"); bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).HasSize(2); // has found one more available hotel }
public void Should_find_matching_and_available_hotels() { var hotelsAdapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, new FakeBus()); hotelsAdapter.LoadHotelFile("New York Sofitel-availabilities.json"); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(hotelsAdapter, hotelsAdapter); var requestedLocation = "New York"; var searchQuery = new SearchBookingOptions(Constants.MyFavoriteSaturdayIn2017, checkOutDate: Constants.MyFavoriteSaturdayIn2017.AddDays(1), location: requestedLocation, numberOfAdults: 2, numberOfRoomsNeeded: 1, childrenCount: 0); var bookingOptions = readFacade.SearchBookingOptions(searchQuery); Check.That(bookingOptions).HasSize(1); var bookingOption = bookingOptions.First(); Check.That(bookingOption.Hotel.Location).IsEqualTo(requestedLocation); Check.That(bookingOption.Hotel.Name).IsEqualTo("New York Sofitel"); Check.That(bookingOption.AvailableRoomsWithPrices).HasSize(13); }
public void Should_execute_Instruction_when_there_is_enough_liquidity_on_the_Markets() { // Given market A: 100 @ $100, market B: 55 @ $100 // When Investor wants to buy 125 stocks @ $100 Then SOR can execute at the requested MarketPrice var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 100, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 55, sellPrice: 100M); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 125, price: 100M); InstructionExecutedDto instructionExecuted = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, null); Check.That(instructionExecuted).HasFieldsWithSameValues(new { Way = Way.Buy, Quantity = 125, Price = 100M }); Check.That(marketA.SellQuantity).IsEqualTo(19); Check.That(marketB.SellQuantity).IsEqualTo(11); }
static void Main(string[] args) { var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 150, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 55, sellPrice: 101M); var investorAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); System.Console.WriteLine("SOR connected to markets: {0} and {1}", marketA.MarketName, marketB.MarketName); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 125, price: 100M); System.Console.WriteLine(); System.Console.WriteLine("Type 'Enter' to submit the following investor instruction: [{0}]\n\n", investorInstructionDto); System.Console.ReadLine(); investorAdapter.Route(investorInstructionDto, arg => { System.Console.WriteLine("Instruction executed: [{0}]", arg); }, eventArgs => {}); System.Console.WriteLine(); System.Console.WriteLine("Type 'Enter' to exit"); System.Console.ReadLine(); }
public void Should_execute_Orders_on_weighted_average_of_available_quantities() { // 25 premier ; 75 % sur le second marché // Given market A: 100 @ $100, market B: 50 @ $100 // When Investor wants to buy 75 stocks @ $100 Then SOR can execute at the requested MarketPrice // And execution is: 50 stocks on MarketA and 25 stocks on MarketB var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 100, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 50, sellPrice: 100M); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 75, price: 100M); InstructionExecutedDto instructionExecuted = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, null); Check.That(instructionExecuted).HasFieldsWithSameValues(new { Way = Way.Buy, Quantity = 75, Price = 100M }); Check.That(marketA.SellQuantity).IsEqualTo(50); Check.That(marketB.SellQuantity).IsEqualTo(25); }
public void DebeActualizar_readmodel_usuario_reservacion_cuando_CancelarPedidoCommand_is_enviadot() { var pedidoEngine = new PedidoYClientesRepository(); var bus = new FakeBus(synchronousPublication: true); CompositionRootHelper.BuildTheWriteModelHexagon(pedidoEngine, pedidoEngine, bus, bus); var pizzeriasYPizzasAdapter = new PizzeriasYPizzasAdapter(Constants.RelativePathForPizzaIntegrationFiles, bus); pizzeriasYPizzasAdapter.LoadAllPizzaFiles(); var reservationAdapter = new ReservaAdapter(bus); CompositionRootHelper.BuildTheReadModelHexagon(pizzeriasYPizzasAdapter, pizzeriasYPizzasAdapter, reservationAdapter, bus); var clienteId = "*****@*****.**"; Check.That(reservationAdapter.GetReservacionesDe(clienteId)).IsEmpty(); var pizzeriaId = 2; var cantidad = 2; var pedidoCommand = new PedidoCommand(clienteId: clienteId, pizzeriaId: pizzeriaId, pizzaNombre: "Peperoni", cantidad: 2, fechaDeEntrega: Constants.MyFavoriteSaturdayIn2019); bus.Send(pedidoCommand); var pedidoGuid = pedidoEngine.GetPedidoDe(clienteId).First().PedidoId; Check.That(reservationAdapter.GetReservacionesDe(clienteId)).HasSize(1); var reservation = reservationAdapter.GetReservacionesDe(clienteId).First(); Check.That(reservation.Cantidad).IsEqualTo(cantidad); Check.That(reservation.PizzeriaId).IsEqualTo(pizzeriaId); var cancelCommand = new CancelarPedidoCommand(pedidoGuid, clienteId); bus.Send(cancelCommand); Check.That(reservationAdapter.GetReservacionesDe(pedidoGuid, clienteId)).HasSize(0); }
public void Should_succeeded_when_liquidity_is_available_even_if_one_Market_rejects() { // Given market A: 150 @ $100, market B: 55 @ $101 // When Investor wants to buy 125 stocks @ $100 Then SOR can execute at the requested MarketPrice var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 50, sellPrice: 100M); var rejectingMarket = new ApiMarketGateway("LSE (London)", sellQuantity: 50, sellPrice: 100M, orderPredicate: _ => false); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, rejectingMarket); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 50, price: 100M, goodTill: DateTime.Now.AddMinutes(5)); // Subscribes to the instruction's events InstructionExecutedDto instructionExecuted = null; InstructionFailedDto instructionFailed = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, (args) => { instructionFailed = args; }); Check.That(instructionExecuted).IsNotNull(); Check.That(instructionFailed).IsNull(); Check.That(marketA.SellQuantity).IsEqualTo(0); Check.That(rejectingMarket.SellQuantity).IsEqualTo(50); }
public void Should_retrieve_updated_list_of_reservations() { var bus = new FakeBus(); var adapter = new HotelsAndRoomsAdapter(Constants.RelativePathForHotelIntegrationFiles, bus); adapter.LoadHotelFile("New York Sofitel-availabilities.json"); var reservationAdapter = new ReservationAdapter(bus); var readFacade = CompositionRootHelper.BuildTheReadModelHexagon(adapter, adapter, reservationAdapter, bus); var clientId = "*****@*****.**"; IEnumerable <Reservation> reservations = readFacade.GetReservationsFor(clientId); Check.That(reservations).IsEmpty(); var bookingAndClientsRepository = new BookingAndClientsRepository(); CompositionRootHelper.BuildTheWriteModelHexagon(bookingAndClientsRepository, bookingAndClientsRepository, bus, bus); var hotelId = 1; var hotelName = "New York Sofitel"; var roomNumber = "101"; var checkInDate = Constants.MyFavoriteSaturdayIn2017; var checkOutDate = checkInDate.AddDays(1); bus.Send(new BookingCommand(clientId, hotelName, hotelId, roomNumber, checkInDate, checkOutDate)); reservations = readFacade.GetReservationsFor(clientId); Check.That(reservations).HasSize(1); var reservation = reservations.First(); Check.That(reservation.ClientId).IsEqualTo(clientId); Check.That(reservation.HotelId).IsEqualTo(hotelId.ToString()); // TODO: make the hotelId a string and not an int. Check.That(reservation.RoomNumber).IsEqualTo(roomNumber); Check.That(reservation.CheckInDate).IsEqualTo(checkInDate); Check.That(reservation.CheckOutDate).IsEqualTo(checkOutDate); }
public void Should_failed_when_Order_exceeds_all_Market_capacity_and_partial_execution_not_allowed() { // Given market A: 150 @ $100, market B: 55 @ $101 // When Investor wants to buy 125 stocks @ $100 Then SOR can execute at the requested MarketPrice var marketA = new ApiMarketGateway("NYSE (New York)", sellQuantity: 15, sellPrice: 100M); var marketB = new ApiMarketGateway("CME (Chicago)", sellQuantity: 55, sellPrice: 101M); var investorInstructionAdapter = CompositionRootHelper.ComposeTheHexagon(marketA, marketB); var investorInstructionDto = new InvestorInstructionDto(Way.Buy, quantity: 125, price: 100M, allowPartialExecution: false); // Subscribes to the instruction's events InstructionExecutedDto instructionExecuted = null; InstructionFailedDto instructionFailed = null; investorInstructionAdapter.Route(investorInstructionDto, (args) => { instructionExecuted = args; }, (args) => { instructionFailed = args; }); // Couldn't execute because order with excessive QuantityOnTheMarket Check.That(instructionFailed.Reason).IsNotNull().And.IsEqualIgnoringCase("Excessive quantity!"); Check.That(instructionExecuted).IsNull(); Check.That(marketA.SellQuantity).IsEqualTo(15); Check.That(marketB.SellQuantity).IsEqualTo(55); }