Assuming you are using a single database for both, I would look at having the database and ORM layer behind a web service that both UIs can consume. That would allow you to put your business logic where it needs to be and decouple things so that the day you need a mobile interface or whatever else, you don't have to write another data access layer. Assuming you are using WCF on a windows stack, some of the native bindings should be as fast as you need them to be and designing for this environment forces some good habits in terms of data management.
Having the facility to put some logic on the service side also saves on having to write anything twice.
The downside is that updating your API affects all your clients, so you might want to have that well defined to start with and it does mean your desktop client and your web api client are tied in more closely to the application as a whole, but given that they are sharing a data store to start with, that is not necessarily a bad thing.
Internal networks often use 1 Gbps connections, or faster. Optical fiber connections or bonding allow much higher bandwidths between the servers. Now imagine the average size of a JSON response from an API. How much of such responses can be transmitted over a 1 Gbps connection in one second?
Let's actually do the math. 1 Gbps is 131 072 KB per second. If an average JSON response is 5 KB (which is quite a lot!), you can send 26 214 responses per second through the wire with just with one pair of machines. Not so bad, isn't it?
This is why network connection is usually not the bottleneck.
Another aspect of microservices is that you can scale easily. Imagine two servers, one hosting the API, another one consuming it. If ever the connection becomes the bottleneck, just add two other servers and you can double the performance.
This is when our earlier 26 214 responses per second becomes too small for the scale of the app. You add other nine pairs, and you are now able to serve 262 140 responses.
But let's get back to our pair of servers and do some comparisons.
If an average non-cached query to a database takes 10 ms., you're limited to 100 queries per second. 100 queries. 26 214 responses. Achieving the speed of 26 214 responses per second requires a great amount of caching and optimization (if the response actually needs to do something useful, like querying a database; "Hello World"-style responses don't qualify).
On my computer, right now, DOMContentLoaded for Google's home page happened 394 ms. after the request was sent. That's less than 3 requests per second. For Programmers.SE home page, it happened 603 ms. after the request was sent. That's not even 2 requests per second. By the way, I have a 100 Mbps internet connection and a fast computer: many users will wait longer.
If the bottleneck is the network speed between the servers, those two sites could literally do thousands of calls to different APIs while serving the page.
Those two cases show that network probably won't be your bottleneck in theory (in practice, you should do the actual benchmarks and profiling to determine the exact location of the bottleneck of your particular system hosted on a particular hardware). The time spent doing the actual work (would it be SQL queries, compression, whatever) and sending the result to the end user is much more important.
Think about databases
Usually, databases are hosted separately from the web application using them. This can raise a concern: what about the connection speed between the server hosting the application and the server hosting the database?
It appears that there are cases where indeed, the connection speed becomes problematic, that is when you store huge amounts of data which don't need to be processed by the database itself and should be available right now (that is large binary files). But such situations are rare: in most cases, the transfer speed is not that big compared to the speed of processing the query itself.
When the transfer speed actually matters is when a company is hosting large data sets on a NAS, and the NAS is accessed by multiple clients at the same time. This is where a SAN can be a solution. This being said, this is not the only solution. Cat 6 cables can support speeds up to 10 Gbps; bonding can also be used to increase the speed without changing the cables or network adapters. Other solutions exist, involving data replication across multiple NAS.
Forget about speed; think about scalability
An important point of a web app is to be able to scale. While the actual performances matter (because nobody wants to pay for more powerful servers), scalability is much more important, because it let you to throw additional hardware when needed.
If you have a not particularly fast app, you'll lose money because you will need more powerful servers.
If you have a fast app which can't scale, you'll lose customers because you won't be able to respond to an increasing demand.
In the same way, virtual machines were a decade ago perceived as a huge performance issue. Indeed, hosting an application on a server vs. hosting it on a virtual machine had an important performance impact. While the gap is much smaller today, it still exists.
Despite this performance loss, virtual environments became very popular because of the flexibility they give.
As with the network speed, you may find that VM is the actual bottleneck and given your actual scale, you will save billions of dollars by hosting your app directly, without the VMs. But this is not what happens for 99.9% of the apps: their bottleneck is somewhere else, and the drawback of a loss of a few microseconds because of the VM is easily compensated by the benefits of hardware abstraction and scalability.
Best Answer
In general, the process to migrate a monolith to a microservice architecture involves carefully extracting parts from the monolith so they can live as stand-alone applications. For example, let's consider a hotel booking system. You have a rental subsystem to allocate rooms, a profile subsystem to persist data about your customers, a billing subsystem, and so on1. This is our starting point.
The monolith most likely evolved organically for a months or years, and all subsystems are not as decoupled as you would wish. From a microservice perspective, this is bad. Our first step will be to take one of those subsystems (usually, the least risky) and to re-architecture it inside the monolith so that it is completely decoupled. In our example, this could possibly be the profile subsystem.
Data access to the customer data is spread everywhere in our code. Our first task is to isolate this subsystem. We'll move the code to a separate library which will expose an interface through which the other subsystems will request data about customers. This is very important: no other subsystem can ask the database directly about customer data. They have to ask the service for this information.
Once this step is done2, the subsystem is ready to be moved to its own service to live on his own. In what's left of the monolith, now, instead of using the library to access customer data, we will call the new stand-alone profile service3. We have our first microservice4.
We accomplished two very important things:
Back to your use case, it seems to me you are working your way backward. By building "microservices" in front of the monolith to proxy calls, you have created two important problems.
You're not gaining the benefits of a microservice architecture, but you still increased the complexity of the system as a whole. Your monolith is still just a big, it's still costly to run your entire test suites and involve QA for every bug fix or feature and you cannot make progress as fast as you would like.
1. In domain-driven design, these subsystems would certainly be bounded contexts.
2. Even though you could extract the subsystem to a separate service right away, it's probably a good idea to wait until its public interface has stabilized through a few features. Once it's extracted to a new microservice, it may be costly to fix mistakes due to placing boundaries incorrectly.
3. This means that even though you may actually use the same instance of a DBMS for all your services, the database of the service A should not be coupled to the database of the service B. For instance, if you had a
customer
table and arental
table and had a foreign keycustomer.id
inrental
, you will have to delete this foreign key and lose this referential integrity enforced by the DBMS. You'll have to ensure consistency in another way. It's the price to pay in a microservice architecture.4. Obviously, I omitted a lot of infrastructure complexity from this answer. Doing microservices isn't a low hanging fruit: make sure it's actually appropriate to your use case!