When I started developing ASP.Net applications (.Net 1.1, around 2004), the world of application development looked simple:

Application connecting to a database and the “universe” (anything it needs to interface to)
(Before that, I developed in mainly Delphi, also in Oracle Forms or ASP/VB, and it always looked like this)
The web application would connect directly to the (SQL Server) database given the support of Visual Studio designers, query builders, etc., and the semi-automatic connection between parameters of database statements and the values of query string parameters, control properties, and so on.
Of course, the “simple” approach had some drawbacks, and I even created a tool called graspx to work around the drawbacks (rather than changing the approach, as I realized later), such as finding the SQL statements retrieving and writing data to figure out database dependencies.
The first step away from the direct application/database connection is to generate a code model based on the database model

Application using a code model representing the database model
Since the code model is derived from the database model, changes in the database model immediately cause the compilation to break if tables or fields are deleted.
However, the application is still monolithic, which restricts the ability to write unit and module tests for each piece of functionality (especially if you chose to develop ASP.Net web sites rather than web applications).
So, let’s separate the business logic from the user interface part (this can be done in classic ASP.Net as well, but it’s trickier. In ASP.Net MVC, you are forced strongly encourage to follow this pattern).

Application using a business layer implementing functionality, communicating with the database via a ORM, and interfacing to other environments using separate libraries.
The business layer may encapsulate various functionality, such as communicating with systems other than the database, handling different data, interfacing to other systems, and so on. We extract the interfacing code to separate assemblies for each system, and route the calls through the business layer assembly.
Usually, the front end application is not the single application accessing the database. You need to add a service that executes asynchronous operations, or write an import or export tool, allow batch processing and so on. These applications do not need the full functionality of the existing business layer (with all its dependencies on other systems), so we create a separate business layer assembly which only provides the data model that application requires:

Application using a business layer implementing functionality, communicating with the database via a ORM, and interfacing to other environments using separate libraries.
This architecture is not even restricted to a certain kind of applications (in my case, web applications with a certain database), you can also apply it to other scenarios:

CRM applications using a business layer implementing functionality, communicating with CRM database via the CRM data classes, and interfacing to other environments using separate libraries.
From my work on CRM projects, I noticed that these projects tend to look like charts 1 and 2. If you start out with the separation of layers and responsibilities, you can easily get a nice architecture, even if it is not obvious from the beginning.