# Conflicts: # npm/ng-packs/package.json # npm/packs/aspnetcore.mvc.ui.theme.shared/package.json # npm/packs/blogging/package.json # npm/packs/bootstrap-daterangepicker/package.json # npm/packs/docs/package.json # npm/packs/jquery-form/package.json # npm/packs/jquery-validation-unobtrusive/package.json # npm/packs/malihu-custom-scrollbar-plugin/package.json # npm/packs/swiper/package.json # npm/packs/timeago/package.json # npm/packs/toastr/package.json # templates/app-nolayers/angular/package.json # templates/app/angular/package.json # templates/module/angular/package.json # templates/module/angular/projects/my-project-name/package.jsonpull/26145/head
@ -0,0 +1,22 @@ |
|||
{ |
|||
"culture": "pt", |
|||
"texts": { |
|||
"Account": "Conta ABP - Iniciar sessão e registar | ABP.IO", |
|||
"Welcome": "Bem-vindo", |
|||
"UseOneOfTheFollowingLinksToContinue": "Utilize uma das seguintes ligações para continuar", |
|||
"FrameworkHomePage": "Página inicial da framework", |
|||
"FrameworkDocumentation": "Documentação da framework", |
|||
"OfficialBlog": "Blogue oficial", |
|||
"CommercialHomePage": "Página inicial comercial", |
|||
"CommercialSupportWebSite": "Site de apoio comercial", |
|||
"CommunityWebSite": "Site da comunidade ABP", |
|||
"ManageAccount": "A minha conta | ABP.IO", |
|||
"ManageYourProfile": "Gerir o seu perfil", |
|||
"ReturnToApplication": "Voltar à aplicação", |
|||
"IdentityUserNotAvailable:Deleted": "Este endereço de email não está disponível. Motivo: já foi eliminado.", |
|||
"SelectYourOrganization": "Selecione a sua organização", |
|||
"PleaseSelectOrganization": "Selecione uma organização para continuar", |
|||
"Continue": "Continuar", |
|||
"CaptchaExplanation": "Calcule a expressão matemática abaixo e introduza a resposta." |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 444 KiB |
@ -0,0 +1,511 @@ |
|||
If you come from plain ASP.NET Core and open your first ABP solution, the initial reaction is often the same: *why are there so many projects, layers, DTOs, interfaces and base classes just to build a simple feature?* 🤔 |
|||
|
|||
That reaction is normal 👌 |
|||
|
|||
ABP Framework gives you a lot on day one: modularity, DDD-friendly structure, application services, repositories, auto API controllers, authorization, auditing, multi-tenancy and UI integration patterns. |
|||
The upside is speed and consistency on serious business apps. |
|||
The downside is that beginners can hit an abstraction wall before they see the payoff. |
|||
|
|||
After reviewing recent discussions, one pattern is clear: most developers do not struggle with C# or ASP.NET Core itself. |
|||
They struggle with *where code is supposed to go* in ABP and *which parts are essential versus optional*. |
|||
|
|||
In this post, I'll focus on that gap. If you are new to ABP, here is what actually helps. |
|||
|
|||
## The biggest learning barrier is not syntax! It is responsibility boundaries |
|||
|
|||
The hardest part for most newcomers is not learning one more framework API. |
|||
It's understanding the architectural split: |
|||
|
|||
- What belongs in the **Domain** layer? |
|||
- What belongs in **Application** services? |
|||
- Why do **DTOs** exist if you already have entities? |
|||
- When do you need a **repository**? |
|||
- Why are there separate projects like `Application.Contracts`, `Domain.Shared` and `EntityFrameworkCore`? |
|||
|
|||
In plain ASP.NET Core apps, many developers put a lot of this logic in controllers, services or even EF Core models. |
|||
ABP forces you toward clearer separation. |
|||
|
|||
A practical mental model: |
|||
|
|||
- **Entity / Aggregate Root**: business state and core invariants |
|||
- **Domain Service**: domain logic that does not naturally belong to a single entity |
|||
- **Repository**: persistence access for aggregates |
|||
- **Application Service**: use-case orchestration, authorization, DTO mapping, transaction boundary |
|||
- **DTO**: data contract for input/output |
|||
- **UI / API layer**: presentation concerns only |
|||
|
|||
> That sounds clean on paper... |
|||
> The confusion starts when you build something real 🥴 |
|||
|
|||
### A simple example: where should validation go? |
|||
|
|||
Suppose you are creating an `Order`. |
|||
|
|||
- If the rule is "order total must be greater than zero," **that's domain logic**. |
|||
- If the rule is "only users with the Orders.Create permission can create an order," **that belongs in the application layer**. |
|||
- If the rule is "customer name is required on this page," **that may exist in DTO validation too**. |
|||
|
|||
New ABP developers often ask which layer owns relationships, validation and business rules. |
|||
|
|||
> The honest answer is: different validation lives in different places. |
|||
|
|||
That is the first ABP lesson worth learning👍 |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
## Why ABP Feels Heavy at First! |
|||
|
|||
**ABP is opinionated**. It's not trying to be the thinnest possible wrapper over ASP.NET Core. |
|||
|
|||
What beginners usually experience as "**too much structure**" comes from 4 things: |
|||
|
|||
### 1. Project and layer count |
|||
|
|||
A typical ABP solution can include: |
|||
|
|||
- `Domain` |
|||
- `Domain.Shared` |
|||
- `Application` |
|||
- `Application.Contracts` |
|||
- `EntityFrameworkCore` |
|||
- `HttpApi` |
|||
- `HttpApi.Client` |
|||
- UI project such as MVC, Razor Pages, Blazor or Angular |
|||
- Test projects |
|||
|
|||
For a small feature, that can feel excessive... **For a long-lived business system, it starts to make sense**. |
|||
|
|||
### 2. Generated convenience hides the mechanics |
|||
|
|||
ABP can generate a lot of CRUD plumbing and generic base classes like `CrudAppService` reduce repetitive code. |
|||
That's useful, but it can also hide how things connect. |
|||
|
|||
<u>A beginner sees a working page and API without fully understanding:</u> |
|||
|
|||
- how the application service is exposed as an API |
|||
- where repository methods are coming from |
|||
- how DTO mapping works |
|||
- why the UI calls application contracts instead of entities |
|||
|
|||
### 3. DDD terminology raises the entry cost |
|||
|
|||
You do not need to become a DDD master to use ABP well... But ABP definitely assumes some familiarity with: |
|||
|
|||
- entities |
|||
- aggregate roots |
|||
- repositories |
|||
- value objects |
|||
- domain services |
|||
- bounded contexts and modules |
|||
|
|||
If those ideas are new, ABP can feel harder than it really is. |
|||
|
|||
### 4. UI integration is not always obvious |
|||
|
|||
Newcomers also get stuck on the end-to-end flow: |
|||
|
|||
1. User clicks a button on a Razor Page or Blazor page |
|||
2. UI sends data to an application service or HTTP API |
|||
3. Application service validates permissions and input |
|||
4. Domain and repository code runs |
|||
5. DTO comes back to the UI |
|||
|
|||
Once you understand that flow, ABP becomes much more predictable. |
|||
|
|||
## Start with CRUD, but do not stop there |
|||
|
|||
A common question is whether beginners should start with simple CRUD or jump straight into a realistic business module. |
|||
|
|||
My view: **start with CRUD, then quickly move to a business feature with real rules**. |
|||
|
|||
### Why CRUD is the right first step |
|||
|
|||
CRUD teaches the ABP basics with low cognitive load: |
|||
|
|||
- project structure |
|||
- entity definition |
|||
- DTOs |
|||
- repositories |
|||
- application services |
|||
- permissions |
|||
- UI page wiring |
|||
- migrations and database updates |
|||
|
|||
This is why the [ABP BookStore tutorial](https://abp.io/docs/latest/tutorials/book-store) is a useful starting point. |
|||
|
|||
### Why CRUD alone is not enough |
|||
|
|||
Pure CRUD can give you a false sense of understanding. |
|||
|
|||
A generated Create / Read / Update / Delete screen does not force you to deal with: |
|||
|
|||
- aggregate boundaries |
|||
- child collections |
|||
- business invariants |
|||
- cross-entity rules |
|||
- domain services |
|||
- richer authorization scenarios |
|||
- multi-tenancy behavior |
|||
- auditing decisions |
|||
|
|||
Those are the areas where ABP starts to justify its structure. |
|||
|
|||
### A better learning sequence |
|||
|
|||
Use this progression: |
|||
|
|||
1. Build one very small CRUD module |
|||
2. Rebuild part of it manually instead of relying only on generation |
|||
3. Build one realistic business module with at least one non-trivial rule |
|||
4. Add authorization, validation and a relationship |
|||
5. Add tests around the domain or application service |
|||
|
|||
That path keeps the early win while exposing the real architecture. |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
## Generated CRUD vs manual CRUD: learn both |
|||
|
|||
This is one of the most useful mindset shifts for ABP beginners. |
|||
|
|||
**Generated CRUD is for productivity. Manual CRUD is for understanding.** |
|||
|
|||
You need both. |
|||
|
|||
### When generated CRUD helps |
|||
|
|||
ABP Suite and ABP base services can save time when the feature is mostly standard admin functionality: |
|||
|
|||
- back-office reference data |
|||
- simple management screens |
|||
- low-risk maintenance pages |
|||
- conventional DTO/entity flows |
|||
|
|||
If the goal is shipping business software efficiently, generated code is not cheating. It is leverage. |
|||
|
|||
### When manual implementation matters |
|||
|
|||
You should manually implement at least one feature end to end so you understand: |
|||
|
|||
- how `CrudAppService` reduces boilerplate |
|||
- what repository methods are doing |
|||
- where validation belongs |
|||
- how authorization is applied |
|||
- how auto API controllers expose application services |
|||
|
|||
A lot of Reddit confusion around ABP comes from learning generated patterns before understanding the underlying manual version. |
|||
|
|||
### A good exercise |
|||
|
|||
Build `Product` management twice: |
|||
|
|||
- First with `CrudAppService` |
|||
- Then manually with custom application service methods and domain rules |
|||
|
|||
Compare both implementations. That single exercise teaches more than reading docs for hours. |
|||
|
|||
## Which DDD patterns real ABP teams often simplify |
|||
|
|||
This is where many beginners get relief: **not every ABP project uses full-strength DDD all the time.** |
|||
|
|||
Real teams often simplify the model, especially early on. |
|||
|
|||
### Patterns teams commonly keep |
|||
|
|||
These tend to deliver value quickly in ABP: |
|||
|
|||
- clear application service boundaries |
|||
- entities and aggregate roots |
|||
- repositories |
|||
- DTO separation |
|||
- modular structure |
|||
- permission-based authorization |
|||
|
|||
### Patterns teams often delay or reduce |
|||
|
|||
These are useful in the right context, but many teams do not force them into every feature: |
|||
|
|||
- dedicated domain services for very simple logic |
|||
- value objects for every tiny concept |
|||
- specification pattern everywhere |
|||
- excessive interface layering where no variation is expected |
|||
- over-splitting modules too early |
|||
|
|||
### A practical rule of thumb |
|||
|
|||
Use the simplest thing that preserves clarity. |
|||
|
|||
For example: |
|||
|
|||
- If a rule is trivial and local to one use case, putting it in an application service may be fine. |
|||
- If a rule protects business invariants and must hold regardless of caller, move it into the domain model. |
|||
- If a concept has behavior and invariants of its own, a value object may help. |
|||
- If it is just a shared enum or constant, `Domain.Shared` is often enough. |
|||
|
|||
ABP supports rich DDD patterns, but it does not require ceremony for ceremony's sake. |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
## A concrete “ASP.NET Core to ABP” learning path |
|||
|
|||
If I had to design a practical learning path for experienced ASP.NET Core developers, it would look like this. |
|||
|
|||
### Step 1: Know what ABP is adding on top of ASP.NET Core |
|||
|
|||
Before touching templates, be comfortable with: |
|||
|
|||
- dependency injection |
|||
- configuration |
|||
- middleware basics |
|||
- EF Core or MongoDB |
|||
- controllers or Razor Pages or Blazor basics |
|||
- validation and authorization in ASP.NET Core |
|||
|
|||
> ABP builds on top of these. It does not replace the need to understand them. |
|||
|
|||
### Step 2: Learn the ABP solution structure |
|||
|
|||
Let's see each layer's goal: |
|||
|
|||
- `Domain`: core business model |
|||
- `Domain.Shared`: shared enums, constants, localization resources, simple shared types |
|||
- `Application.Contracts`: DTOs and service contracts |
|||
- `Application`: use cases and orchestration |
|||
- `EntityFrameworkCore`: database mappings and repository implementation details |
|||
- `HttpApi`: API exposure |
|||
- UI project: user interaction |
|||
|
|||
### Step 3: Understand modules and dependencies |
|||
|
|||
ABP's modularity is a major feature, but beginners often treat modules like folders with extra steps. |
|||
|
|||
They are more than that. |
|||
|
|||
A module defines: |
|||
|
|||
- dependency boundaries |
|||
- service registration scope |
|||
- reusable feature packaging |
|||
- initialization points via module lifecycle methods and `[DependsOn]` |
|||
|
|||
At first, use modules as organizational boundaries inside a modular monolith. Do not rush into distributed or microservice-style decomposition. |
|||
|
|||
### Step 4: Build one CRUD feature the ABP way |
|||
|
|||
Create a simple feature such as Books, Products or Categories. |
|||
|
|||
Make sure you understand: |
|||
|
|||
- entity creation |
|||
- migration flow |
|||
- DTO mapping |
|||
- application service methods |
|||
- permission checks |
|||
- how the UI or API calls the application layer |
|||
|
|||
### Step 5: Rebuild one part manually |
|||
|
|||
Now remove the training wheels for one feature. |
|||
|
|||
Instead of only leaning on base classes, explicitly write: |
|||
|
|||
- a custom application service method |
|||
- a custom repository query if needed |
|||
- domain validation or invariants |
|||
- a tailored DTO instead of generic CRUD shapes |
|||
|
|||
This is where ABP usually clicks. |
|||
|
|||
### Step 6: Build a realistic business module |
|||
|
|||
A good example is `Order Management`, `Leave Requests` or `Inventory Transfer`. |
|||
|
|||
Choose something with: |
|||
|
|||
- one-to-many relationship |
|||
- status transitions |
|||
- authorization rules |
|||
- at least one business invariant |
|||
- audit visibility |
|||
|
|||
That reveals why ABP's layered structure exists. |
|||
|
|||
### Step 7: Add built-in ABP concerns on purpose |
|||
|
|||
ABP shines when you use its built-in platform features intentionally: |
|||
|
|||
- authorization |
|||
- auditing |
|||
- validation |
|||
- localization |
|||
- multi-tenancy |
|||
- settings and permissions |
|||
|
|||
Do not treat these as advanced extras. They are part of the framework's real value. |
|||
|
|||
### Step 8: Learn testing by layer |
|||
|
|||
Even if you do not build a full testing strategy immediately, understand the testing shape: |
|||
|
|||
- domain tests for invariants and business rules |
|||
- application tests for use cases and permissions |
|||
- integration tests for persistence and module wiring |
|||
|
|||
A lot of ABP's architecture pays off once you start testing behavior in isolation. |
|||
|
|||
## A small example of responsibility split |
|||
|
|||
Here is a deliberately small example to make the layering less abstract. |
|||
|
|||
Suppose you have a leave request system. |
|||
|
|||
**Domain** concerns: |
|||
|
|||
- a leave request cannot be approved after rejection |
|||
- end date cannot be before start date |
|||
- total leave days must be positive |
|||
|
|||
**Application** concerns: |
|||
|
|||
- only managers can approve requests |
|||
- map input DTO to entity operations |
|||
- return a DTO shaped for the UI |
|||
- coordinate repository access and unit of work |
|||
|
|||
**UI** concerns: |
|||
|
|||
- disable approve button when user lacks permission |
|||
- show validation messages |
|||
- render status badges and filters |
|||
|
|||
That split is the heart of ABP. Once you start seeing features this way, the framework becomes much easier to navigate. |
|||
|
|||
## When to use ABP and when not to |
|||
|
|||
**ABP is powerful, but <u>it is not automatically the right default</u> for every ASP.NET Core project.** |
|||
|
|||
### When to use ABP |
|||
|
|||
ABP is a strong fit when you are building: |
|||
|
|||
- line-of-business applications |
|||
- admin-heavy platforms |
|||
- SaaS or multi-tenant systems |
|||
- modular monoliths that may grow over time |
|||
- systems that need built-in authorization, auditing, localization and consistent conventions |
|||
- teams that benefit from standardized architecture |
|||
|
|||
### When NOT to use ABP |
|||
|
|||
ABP may be excessive in the following situations: |
|||
|
|||
- a tiny API with minimal business logic |
|||
- a short-lived internal tool where framework structure would dominate the workload |
|||
- a team with no interest in layered architecture or DDD-style thinking |
|||
- a highly custom architecture where ABP conventions would mostly be bypassed |
|||
|
|||
The main cost of ABP is not performance or syntax 🤜 It is **architectural overhead**. |
|||
<u>If the app is too small, that overhead may not pay back.</u> |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Common mistakes new ABP developers make |
|||
|
|||
These are the mistakes I see most often in early ABP learning. |
|||
|
|||
### 1. Trying to understand everything before building anything |
|||
|
|||
Do not wait until every project, package and abstraction makes sense. Build one feature first. |
|||
|
|||
### 2. Using generated code without reading it |
|||
|
|||
Generated CRUD is useful, but inspect what it created. Otherwise you will stay dependent on tooling. |
|||
|
|||
### 3. Forcing textbook DDD into every feature |
|||
|
|||
Not every screen needs aggregates, value objects, domain services and custom repositories all at once. |
|||
|
|||
### 4. Putting all business logic in application services |
|||
|
|||
This works for a while, but you eventually lose domain consistency. Protect important invariants closer to the domain model. |
|||
|
|||
### 5. Splitting into too many modules too early |
|||
|
|||
Start with a modular monolith mindset. Extract boundaries when they become meaningful. |
|||
|
|||
### 6. Ignoring built-in ABP features |
|||
|
|||
If you manually rebuild authorization, auditing or tenant-aware behavior without understanding ABP's built-ins, you are fighting the framework. |
|||
|
|||
|
|||
|
|||
## The learning path I would actually recommend to a new team |
|||
|
|||
If a team asked me for a practical ABP onboarding sequence, I would keep it simple: |
|||
|
|||
### 📚 Week 1: Basics and orientation |
|||
|
|||
- Review ABP solution structure |
|||
- Build the BookStore-style tutorial once |
|||
- Identify what each layer is responsible for |
|||
|
|||
### 📚 Week 2: Manual feature implementation |
|||
|
|||
- Build one small module manually |
|||
- Avoid too much generation |
|||
- Trace one request from UI to application service to repository to database |
|||
|
|||
### 📚 Week 3: Real business rules |
|||
|
|||
- Add relationships |
|||
- Add authorization |
|||
- Add a workflow or state transition |
|||
- Write tests for a few business rules |
|||
|
|||
### 📚 Week 4: Productivity and conventions |
|||
|
|||
- Reintroduce generated tooling where it saves time |
|||
- Standardize module patterns |
|||
- Decide which DDD patterns the team will use by default and which are optional |
|||
|
|||
That sequence teaches both the architecture and the productivity side of ABP. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Final perspective: learn the intent, not just the template |
|||
|
|||
ABP can feel complicated when approached as a collection of projects and base classes. It gets easier when you see the intent behind the structure: |
|||
|
|||
- protect business rules |
|||
- standardize application boundaries |
|||
- make common enterprise features reusable |
|||
- keep large apps maintainable |
|||
|
|||
If you are new to ABP, do not aim to master every pattern immediately. Aim to answer these four questions clearly for each feature: |
|||
|
|||
- What is the business rule? |
|||
- Which layer owns it? |
|||
- What data crosses the boundary? |
|||
- Which ABP feature already solves part of this problem? |
|||
|
|||
Once those answers become natural, ABP stops feeling heavy and starts feeling productive. |
|||
|
|||
--- |
|||
|
|||
## As a Summary |
|||
|
|||
- **The biggest ABP learning barrier is** understanding responsibility boundaries between domain, application services, DTOs, repositories and UI. |
|||
- **Start with a small CRUD feature**, but move quickly to a realistic business module with rules, relationships and permissions. |
|||
- **Learn both generated and manual CRUD**; one gives productivity, the other gives understanding. |
|||
- Real ABP teams often simplify DDD and adopt advanced patterns **only when the complexity justifies them**. |
|||
- **The best learning path is ASP.NET Core basics first**, then ABP layers, one manual feature, one real module and built-in features like authorization and auditing. |
|||
|
After Width: | Height: | Size: 1010 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 162 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 2.0 MiB |
@ -0,0 +1,308 @@ |
|||
For years, many .NET teams treated core open source libraries as stable background infrastructure: useful, battle-tested, and effectively free forever. That assumption is starting to break. |
|||
|
|||
When widely used projects like IdentityServer, AutoMapper, and MediatR move toward commercial or more restrictive licensing, the discussion is no longer just about one package or one maintainer. It becomes a bigger question about how the .NET ecosystem pays for the software it depends on. |
|||
|
|||
This matters because modern .NET applications are built on layers of third-party dependencies. If one of those layers changes its pricing, support model, or license terms, the impact is not theoretical. It affects procurement, architecture, upgrade strategy, compliance, and long-term maintenance. |
|||
|
|||
## The pattern is no longer isolated |
|||
|
|||
A few years ago, licensing changes in .NET could still be dismissed as exceptions. That is getting harder. |
|||
|
|||
Several important projects now illustrate the same underlying tension: software that became critical infrastructure was often maintained with a funding model better suited to side projects than production-critical systems. |
|||
|
|||
## Duende IdentityServer is the clearest example |
|||
|
|||
The IdentityServer story is probably the most visible case in .NET. What started as a widely adopted open-source identity solution evolved into Duende IdentityServer, which requires paid licenses for production use, while free usage is limited to development, testing, personal projects, or qualifying community scenarios. |
|||
|
|||
More recently, Duende moved again with its v8 generation and introduced a more tiered licensing model, including Lite, Standard, Advanced, and Custom options, plus paid add-ons for additional capabilities. At the same time, support windows are clearly tied to .NET versions, making the product feel even more like managed commercial infrastructure than community software. |
|||
|
|||
> That is not necessarily a bad thing. Identity is security-critical software. It is expensive to maintain, expensive to support, and risky to underfund. But it does mark a major shift in expectations for teams that still think of it primarily as an OSS building block. |
|||
|
|||
## AutoMapper and MediatR point to a broader shift |
|||
|
|||
AutoMapper and MediatR are different kinds of libraries, but their direction matters just as much. |
|||
|
|||
These are not niche components. They are deeply embedded in enterprise codebases, tutorials, templates, and architectural conventions. So when they move toward dual licensing, commercial terms, or more restrictive licensing, the message is clear: **even highly popular and culturally central .NET libraries may no longer fit the old “free and permissive forever” model.** |
|||
|
|||
AutoMapper’s move away from .NET Foundation membership after adopting a non-permissive license is especially notable because it highlights a governance boundary. The ecosystem may celebrate OSS, but institutions such as the .NET Foundation still rely on clear licensing rules. Once a project changes those terms, it often changes its place in the ecosystem too. |
|||
|
|||
## Why maintainers are doing this |
|||
|
|||
The easy reaction is to call commercialization a betrayal. The more honest reaction is to admit that many maintainers have been subsidizing the industry for years. |
|||
|
|||
A project can be free for users and still very expensive for its authors. |
|||
Maintaining a popular library often means: |
|||
|
|||
 |
|||
|
|||
Once a library becomes critical infrastructure, users expect reliability similar to commercial software. But expectations usually rise faster than funding. |
|||
|
|||
That imbalance creates a predictable outcome: maintainers either burn out, slow down, seek sponsorship, or commercialize. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Open-source popularity does not automatically create sustainability |
|||
|
|||
This is the part many teams still underestimate. |
|||
|
|||
A package can have massive adoption and still be financially fragile. Downloads, GitHub stars, and conference mentions do not pay for maintenance. In fact, popularity often increases the burden without improving sustainability. |
|||
|
|||
From a maintainer’s perspective, commercialization can be a rational correction: |
|||
|
|||
- charge the organizations getting the most value |
|||
- fund long-term maintenance |
|||
- offer support contracts and SLAs |
|||
- justify time spent on roadmap work |
|||
- reduce dependence on unpaid labor |
|||
|
|||
### In other words, the move to commercial licensing is often less about greed than about replacing an unrealistic business model. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Why the community reaction is so mixed |
|||
|
|||
Even if the economics make sense, the backlash is real. And frankly, some of it is justified. |
|||
|
|||
The friction usually comes from the gap between legal reality and social expectation. |
|||
|
|||
### Legally, maintainers can often change how future versions are licensed. Socially, users feel that a trusted community dependency has changed the rules after becoming embedded in thousands of systems |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## What bothers teams most |
|||
|
|||
In practice, teams react to more than cost. They are reacting to uncertainty. |
|||
The common concerns are familiar: |
|||
|
|||
- unexpected licensing costs appearing in mature products |
|||
- fear of future pricing increases |
|||
- procurement delays for something developers previously installed with `dotnet add package` |
|||
- license compatibility and compliance reviews |
|||
- vendor lock-in around foundational infrastructure |
|||
- migration costs if a team decides to leave later |
|||
- concern that previously core features move behind paid tiers |
|||
|
|||
This is why the strongest reactions usually happen when the library is infrastructural rather than optional. Authentication, mapping, messaging, and mediator patterns sit close to the core of many architectures. **Replacing them is possible, but rarely cheap.** |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Suddenness matters as much as pricing |
|||
|
|||
A reasonable commercial model can still create anger if the transition feels abrupt. Teams generally accept that maintainers need funding. What they do not accept as easily is: |
|||
|
|||
- vague roadmap communication |
|||
- surprise license changes |
|||
- unclear grandfathering rules |
|||
- unclear distinctions between old and new versions |
|||
- feature packaging that feels like a trap for existing users |
|||
|
|||
That trust dimension matters. In OSS, the license is not the whole relationship. Predictability is part of the product. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## What this signals for the .NET ecosystem |
|||
|
|||
The larger lesson is not simply that some maintainers want to get paid. It is that the .NET ecosystem is maturing into one where critical libraries are increasingly treated like products, not just repositories. That has several consequences. |
|||
|
|||
## 1. Dependency selection is now a governance decision |
|||
|
|||
Choosing a package is no longer only a technical choice. |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
This does not mean avoiding all commercially backed OSS. It means evaluating dependencies the same way you evaluate databases, cloud services, or authentication providers. |
|||
|
|||
## 2. Foundation membership and community trust will matter more |
|||
|
|||
When a project leaves a permissive governance environment, it sends a signal, even if the software remains technically strong. |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
The .NET Foundation’s stance on permissive licensing creates a useful boundary here. It does not solve commercialization, but it helps clarify which projects still fit traditional OSS expectations. |
|||
|
|||
## 3. Forks and alternatives will become more common |
|||
|
|||
When licensing changes upset users, forks appear. That is a normal OSS response. |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
> A reactive fork may help teams buy time, but it does not automatically become sustainable infrastructure. |
|||
|
|||
In many cases, the fork inherits the same funding problem that triggered the original commercialization. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## The practical risk for engineering teams |
|||
|
|||
The biggest mistake teams can make is treating this as community drama instead of delivery risk. |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
This is especially relevant for organizations with long-lived internal platforms or multi-tenant SaaS products, where one dependency can affect dozens of services. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## A realistic example |
|||
|
|||
Imagine a company running an internal platform and several customer-facing .NET applications. |
|||
|
|||
- **The identity layer uses IdentityServer.** |
|||
- **Multiple services use MediatR for application-layer orchestration.** |
|||
- **Older codebases rely heavily on AutoMapper profiles.** |
|||
|
|||
If all three become cost, licensing, or governance concerns at the same time, the company suddenly has a portfolio-level problem rather than a package-level problem. |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
That is architecture, budgeting, and compliance converging in one decision. |
|||
|
|||
## How teams should respond |
|||
|
|||
> Panic is not useful. Blind trust is not useful either. |
|||
|
|||
A better response is to become more deliberate about dependency management. |
|||
|
|||
--- |
|||
|
|||
## Build a dependency review habit |
|||
|
|||
For critical packages, review more than API quality. |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
If a package sits in authentication, authorization, persistence, messaging, or application architecture, the review should be stricter than for a small utility library. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Categorize dependencies by replacement cost |
|||
|
|||
Not every package deserves the same scrutiny. |
|||
|
|||
**A useful model is:** |
|||
|
|||
- low replacement cost: small utilities, isolated helpers |
|||
- medium replacement cost: libraries used across one bounded context |
|||
- high replacement cost: foundational cross-cutting libraries used everywhere |
|||
|
|||
Commercialization risk matters most in the third category. If replacing the library means touching every service, pipeline, or authentication flow, that risk belongs on the architecture radar early., |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## Budget for critical OSS |
|||
|
|||
Many companies are comfortable paying for cloud hosting but still resist paying for the libraries that shape their actual application architecture. |
|||
|
|||
That mindset is becoming outdated. |
|||
|
|||
If a dependency is business-critical, teams should assume one of these will eventually be required: |
|||
|
|||
Press enter or click to view image in full size |
|||
|
|||
 |
|||
|
|||
> You will pay somehow! |
|||
> The only real question is whether you pay proactively or reactively. |
|||
|
|||
--- |
|||
|
|||
## When to use commercially backed OSS and when not to ⛔ |
|||
|
|||
Commercialization is not automatically a reason to avoid a project. |
|||
|
|||
## ✔ WHEN TO USE IT |
|||
|
|||
**Commercially backed OSS can be a good fit when:** |
|||
|
|||
 |
|||
|
|||
Identity infrastructure is the obvious example. A mature, well-supported identity product may be worth paying for if the alternative is building and maintaining security-sensitive code yourself. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## ⛔ WHEN NOT TO USE IT |
|||
|
|||
**Be cautious when:** |
|||
|
|||
 |
|||
|
|||
This is where some teams may rethink packages like object mappers or mediator frameworks. If the dependency is mostly ergonomic and the long-term governance risk is rising, simpler code may be the better tradeoff. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## What this means for maintainers, companies, and the community |
|||
|
|||
The ecosystem now needs more honest expectations on all sides. |
|||
|
|||
## * For maintainers |
|||
|
|||
If your library underpins production systems, sustainability needs to be part of the strategy early. Commercialization is easier to accept when it is transparent, gradual, and communicated as part of a long-term model rather than a sudden pivot. |
|||
|
|||
## * For companies |
|||
|
|||
If your business depends on OSS, treating maintainers as an infinite free resource is no longer credible. Critical dependencies should have owners, budgets, and risk reviews. |
|||
|
|||
## * For the .NET community |
|||
|
|||
The community may need to become more selective about what it normalizes as default architecture. If a pattern depends heavily on a few centralized libraries, then a licensing change in one project can ripple widely. Simpler stacks are often more resilient. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## A likely next phase for .NET OSS |
|||
|
|||
The next few years will probably bring more segmentation across the .NET ecosystem. |
|||
|
|||
Expect to see more of this: |
|||
|
|||
 |
|||
|
|||
That does not mean open source in .NET is weakening. It means the ecosystem is facing the same sustainability pressures seen elsewhere: maintenance is expensive, infrastructure software has real business value, and someone eventually has to fund it. |
|||
|
|||
The healthiest outcome is not pretending commercialization should never happen. It is making sure it happens with predictable governance, fair communication, and realistic expectations from users. |
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## SUMMARY |
|||
|
|||
- Commercialization of key .NET libraries is a sustainability signal, not an isolated incident. |
|||
- Teams should evaluate dependencies by license, governance, support policy, and replacement cost. |
|||
- Commercial OSS can be the right choice for critical infrastructure, especially where support and security matter. |
|||
- The real risk is not paying for software; it is being surprised by cost, lock-in, or migration pressure too late. |
|||
- .NET teams should treat dependency strategy as an architectural and business decision, not just a NuGet decision. |
|||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 44 KiB |
@ -0,0 +1,601 @@ |
|||
Most monoliths do not fail because they are monoliths. They fail because they become tangled. |
|||
|
|||
That is exactly why the modular monolith is such a practical architecture for business applications. You keep the operational simplicity of a single deployment, but you organize the codebase around clear business boundaries. With ABP Studio, this approach is not an afterthought. It is built into the way you create and evolve a solution. |
|||
|
|||
In this article, I will walk through how to build a modular monolith with ABP Studio, how ABP modules fit together, where teams usually get the boundaries wrong, and how to structure your solution so it stays maintainable as it grows. |
|||
|
|||
If you are building a line-of-business app and want something more disciplined than a traditional monolith, but less expensive than microservices, this is one of the strongest options in the .NET ecosystem. |
|||
|
|||
## Why a modular monolith fits many real projects |
|||
|
|||
A modular monolith gives you: |
|||
|
|||
- one deployable application |
|||
- one main runtime host |
|||
- clear module boundaries by business capability |
|||
- the option to evolve selected modules later |
|||
- less distributed systems overhead than microservices |
|||
|
|||
That trade-off matters in real teams. Most products do not need network boundaries on day one. They need: |
|||
|
|||
- faster delivery |
|||
- simpler debugging |
|||
- less infrastructure |
|||
- a codebase that does not collapse after six months |
|||
|
|||
ABP Framework is designed around modularity. A module in ABP can own its own: |
|||
|
|||
- domain model |
|||
- application services |
|||
- database integration |
|||
- API endpoints |
|||
- UI pieces |
|||
- tests |
|||
|
|||
That makes ABP a natural fit for modular monolith architecture rather than a framework you have to bend into shape. |
|||
|
|||
## What ABP Studio creates for a modular monolith |
|||
|
|||
When you choose the Modular Monolith option in ABP Studio's New Solution Wizard, ABP creates a solution structure intended for a modern modular application. |
|||
|
|||
At a high level, you typically get: |
|||
|
|||
- `main/` for the main host application |
|||
- `modules/` for business modules |
|||
- `etc/` for shared infrastructure and configuration assets |
|||
|
|||
This is a useful default because it separates the host from the business capabilities from the start. |
|||
|
|||
A simplified layout looks like this: |
|||
|
|||
```text |
|||
src/ |
|||
main/ |
|||
MyCompany.MyProduct.Web |
|||
MyCompany.MyProduct.HttpApi.Host |
|||
modules/ |
|||
Catalog/ |
|||
MyCompany.MyProduct.Catalog.Domain |
|||
MyCompany.MyProduct.Catalog.Application |
|||
MyCompany.MyProduct.Catalog.EntityFrameworkCore |
|||
MyCompany.MyProduct.Catalog.HttpApi |
|||
MyCompany.MyProduct.Catalog.Web |
|||
Ordering/ |
|||
MyCompany.MyProduct.Ordering.Domain |
|||
MyCompany.MyProduct.Ordering.Application |
|||
MyCompany.MyProduct.Ordering.EntityFrameworkCore |
|||
MyCompany.MyProduct.Ordering.HttpApi |
|||
MyCompany.MyProduct.Ordering.Web |
|||
etc/ |
|||
docker/ |
|||
k8s/ |
|||
configs/ |
|||
``` |
|||
|
|||
The exact projects depend on your choices, but the important idea is consistent: the host app lives in `main`, and business capabilities live under `modules`. |
|||
|
|||
ABP Studio also lets you choose modules up front or add them later. That is important because most teams do not know their final module map on day one. You can start with a few strong boundaries and evolve from there. |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
## Understanding ABP modules in practice |
|||
|
|||
In ABP, modules are first-class building blocks. They are not just folders. |
|||
|
|||
A module typically declares dependencies using attributes such as `DependsOn`, which tells ABP how pieces should be initialized and wired together. |
|||
|
|||
A minimal example looks like this: |
|||
|
|||
```csharp |
|||
using Volo.Abp.Modularity; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpDddDomainModule) |
|||
)] |
|||
public class CatalogDomainModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
That may look small, but it is central to the architecture. Dependencies are explicit, and the framework uses those module relationships during startup. |
|||
|
|||
In practical terms, this gives you: |
|||
|
|||
- a consistent module lifecycle |
|||
- explicit compile-time references |
|||
- less hidden coupling |
|||
- clearer ownership boundaries |
|||
|
|||
ABP also distinguishes between framework modules and your application modules. |
|||
|
|||
- Framework modules provide infrastructure features like validation, caching, permission management, and persistence integration. |
|||
- Application modules represent your business capabilities like Catalog, Ordering, Billing, or Support. |
|||
|
|||
Structurally, they are similar. The difference is their role in the system. |
|||
|
|||
## A practical module structure that scales |
|||
|
|||
One of the most useful ABP practices is layered modules. Instead of throwing everything into a single project, you separate concerns inside each module. |
|||
|
|||
A common structure is: |
|||
|
|||
- Domain |
|||
- Application |
|||
- Infrastructure or provider-specific persistence |
|||
- HttpApi |
|||
- Web or UI |
|||
- Tests |
|||
|
|||
For example, a Catalog module may look like this: |
|||
|
|||
### Domain |
|||
|
|||
This is where business rules live: |
|||
|
|||
- entities |
|||
- value objects |
|||
- domain services |
|||
- domain events |
|||
- repository interfaces |
|||
|
|||
Keep this layer focused on business behavior, not framework plumbing. |
|||
|
|||
### Application |
|||
|
|||
This layer orchestrates use cases: |
|||
|
|||
- application services |
|||
- DTOs |
|||
- authorization checks |
|||
- transaction boundaries |
|||
- coordination across domain objects |
|||
|
|||
This is usually where external callers interact with the module. |
|||
|
|||
### EntityFrameworkCore or MongoDB |
|||
|
|||
This layer handles persistence details: |
|||
|
|||
- DbContext or Mongo collections |
|||
- repository implementations |
|||
- mappings |
|||
- migrations where relevant |
|||
|
|||
ABP supports different providers, and a module can include the provider projects it actually needs. |
|||
|
|||
### HttpApi |
|||
|
|||
This exposes the module over HTTP when needed: |
|||
|
|||
- controllers |
|||
- remote service contracts |
|||
- serialization-related setup |
|||
|
|||
### Web |
|||
|
|||
If your solution includes server-side or MVC-style UI integration, this is where UI pieces for the module can live. |
|||
|
|||
### Tests |
|||
|
|||
A solid module usually has separate tests for: |
|||
|
|||
- domain logic |
|||
- application logic |
|||
- persistence integration |
|||
|
|||
For EF Core, in-memory SQLite is a practical option for provider-level tests. For MongoDB, ephemeral test instances are a common approach. |
|||
|
|||
## Step-by-step: creating a modular monolith with ABP Studio |
|||
|
|||
The tooling matters because architecture tends to decay when it is inconvenient. ABP Studio reduces that friction. |
|||
|
|||
A practical setup flow looks like this. |
|||
|
|||
### 1. Create the solution with the Modular Monolith template |
|||
|
|||
In ABP Studio: |
|||
|
|||
- create a new solution |
|||
- choose the Modular Monolith template |
|||
- select your UI and database preferences |
|||
- decide which business modules you want to include initially |
|||
|
|||
This gives you the host app under `main` and a `modules` area for business capabilities. |
|||
|
|||
### 2. Start with business boundaries, not technical layers |
|||
|
|||
Before adding modules, identify your real capabilities. Good early candidates are usually things like: |
|||
|
|||
- Catalog |
|||
- Ordering |
|||
- Inventory |
|||
- Customer Management |
|||
- Billing |
|||
|
|||
Bad module boundaries are usually technical buckets like: |
|||
|
|||
- Utilities |
|||
- Common Business Logic |
|||
- Shared Services |
|||
|
|||
Those become dumping grounds fast. |
|||
|
|||
A simple rule helps: if a module name would make sense to a product owner, it is probably closer to the right boundary. |
|||
|
|||
### 3. Add modules incrementally |
|||
|
|||
You do not need to model the whole enterprise on day one. |
|||
|
|||
Start with two or three meaningful modules. For example: |
|||
|
|||
- Catalog manages products and pricing rules |
|||
- Ordering manages carts, orders, and order state |
|||
- Identity handles users and permissions via ABP's existing modules |
|||
|
|||
This is enough to validate your architecture without over-designing it. |
|||
|
|||
### 4. Keep each module independently understandable |
|||
|
|||
A developer should be able to open `modules/Catalog` and understand: |
|||
|
|||
- what the module owns |
|||
- what it exposes publicly |
|||
- what it depends on |
|||
- how it is tested |
|||
|
|||
If the module constantly reaches into another module's internals, the boundary is already weak. |
|||
|
|||
### 5. Wire modules through explicit dependencies |
|||
|
|||
ABP's module system encourages declaring dependencies up front. |
|||
|
|||
For example, an application layer may depend on its own domain layer and some framework modules: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(CatalogDomainModule), |
|||
typeof(AbpDddApplicationModule) |
|||
)] |
|||
public class CatalogApplicationModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
This is much healthier than hidden runtime coupling or random service lookups scattered across the codebase. |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
## How modules should communicate |
|||
|
|||
This is where many modular monoliths either stay clean or slowly become a distributed mess inside one process. |
|||
|
|||
In ABP, module communication generally falls into two categories: |
|||
|
|||
- synchronous communication through interfaces or public application services |
|||
- asynchronous communication through events |
|||
|
|||
Both are useful. The mistake is using one for everything. |
|||
|
|||
### Option 1: synchronous calls for direct business workflows |
|||
|
|||
Use direct service calls when: |
|||
|
|||
- one module needs an immediate answer |
|||
- the workflow is naturally request-response |
|||
- the dependency is acceptable and explicit |
|||
|
|||
Example: |
|||
|
|||
- Ordering needs to verify product availability from Catalog before creating an order line. |
|||
|
|||
In that case, a clear application service contract is often the simplest solution. |
|||
|
|||
Benefits: |
|||
|
|||
- easy to trace |
|||
- easier to debug |
|||
- strong flow control |
|||
- fewer hidden side effects |
|||
|
|||
Costs: |
|||
|
|||
- tighter coupling between modules |
|||
- dependency direction must be managed carefully |
|||
|
|||
### Option 2: events for decoupled reactions |
|||
|
|||
Use events when: |
|||
|
|||
- a module publishes something that others may react to |
|||
- the publisher should not know all consumers |
|||
- eventual consistency is acceptable |
|||
|
|||
Example: |
|||
|
|||
- Ordering publishes `OrderPlaced` |
|||
- Inventory reserves stock |
|||
- Billing starts invoicing |
|||
- Notifications sends a confirmation |
|||
|
|||
Benefits: |
|||
|
|||
- lower direct coupling |
|||
- easier to add new consumers later |
|||
- better long-term separation |
|||
|
|||
Costs: |
|||
|
|||
- debugging is harder |
|||
- side effects are less obvious |
|||
- too many events can create implicit dependencies |
|||
|
|||
A good default is simple: |
|||
|
|||
- use direct calls for core request-response flows |
|||
- use events for reactions and cross-cutting side effects |
|||
|
|||
## An example module interaction design |
|||
|
|||
Imagine a small commerce system with Catalog and Ordering modules. |
|||
|
|||
### Catalog owns |
|||
|
|||
- products |
|||
- product pricing |
|||
- availability rules |
|||
|
|||
### Ordering owns |
|||
|
|||
- carts |
|||
- orders |
|||
- order state transitions |
|||
|
|||
A clean interaction might look like this: |
|||
|
|||
1. A user places an order through Ordering. |
|||
2. Ordering calls a public Catalog service to validate selected products. |
|||
3. Ordering creates the order in its own domain. |
|||
4. Ordering publishes an order-created event. |
|||
5. Other modules react as needed. |
|||
|
|||
Notice what does not happen: |
|||
|
|||
- Ordering does not directly query Catalog tables. |
|||
- Catalog does not modify Ordering aggregates. |
|||
- Shared internal entities are not passed around freely. |
|||
|
|||
That discipline matters more than the fact that everything runs in one process. |
|||
|
|||
## Database design in a modular monolith |
|||
|
|||
A modular monolith does not force a single database strategy. |
|||
|
|||
With ABP, you can support: |
|||
|
|||
- a shared database for the whole application |
|||
- separate schemas per module |
|||
- module-specific databases in some cases |
|||
|
|||
For most teams, the best starting point is a single database with clear ownership boundaries in code. |
|||
|
|||
Why this is usually the right default: |
|||
|
|||
- simpler operations |
|||
- easier local development |
|||
- straightforward transactions |
|||
- less infrastructure overhead |
|||
|
|||
But even with one database, treat data ownership seriously. |
|||
|
|||
That means: |
|||
|
|||
- each module owns its own tables and mappings |
|||
- cross-module table access is avoided |
|||
- modules interact through services or events, not direct persistence shortcuts |
|||
|
|||
If you later decide to extract a module into a separate service, this discipline will matter far more than whether you started with one database or three. |
|||
|
|||
|
|||
|
|||
 |
|||
|
|||
## When to use layered modules and when not to overdo them |
|||
|
|||
ABP encourages a layered structure because it scales well, but you should still apply judgment. |
|||
|
|||
### Use layered modules when |
|||
|
|||
- the module has real business complexity |
|||
- multiple developers will work on it |
|||
- you want clear separation between domain, use cases, and persistence |
|||
- the module may grow into a reusable building block |
|||
|
|||
### Do not over-layer when |
|||
|
|||
- the module is tiny and stable |
|||
- the behavior is simple CRUD with little business logic |
|||
- extra projects would create more ceremony than clarity |
|||
|
|||
There is no prize for turning a 300-line feature into six projects. |
|||
|
|||
A useful practical rule: |
|||
|
|||
- start simple, but not sloppy |
|||
- add more structure when the module earns it |
|||
|
|||
ABP makes layered modules easy, but that does not mean every feature deserves the full treatment immediately. |
|||
|
|||
## Testing strategy for a modular monolith |
|||
|
|||
Modular architecture only pays off if modules can be tested with confidence. |
|||
|
|||
A practical testing setup includes: |
|||
|
|||
### Domain tests |
|||
|
|||
Use these for pure business rules: |
|||
|
|||
- invariants |
|||
- state transitions |
|||
- validation rules |
|||
- domain service behavior |
|||
|
|||
These should be fast and framework-light. |
|||
|
|||
### Application tests |
|||
|
|||
Use these for use cases: |
|||
|
|||
- application service behavior |
|||
- authorization checks |
|||
- DTO mapping expectations |
|||
- orchestration across domain objects |
|||
|
|||
### Persistence tests |
|||
|
|||
Use these for provider-specific concerns: |
|||
|
|||
- EF Core mappings |
|||
- repository behavior |
|||
- query correctness |
|||
- migration-related assumptions |
|||
|
|||
In ABP-based solutions, this usually means separate test projects per layer or concern. That keeps failures localized and makes refactoring safer. |
|||
|
|||
## Common mistakes that break modular monoliths |
|||
|
|||
The architecture is solid, but the failure modes are predictable. |
|||
|
|||
### 1. Fake modules with real coupling |
|||
|
|||
This is the most common problem. Teams create module folders, but the code still behaves like one giant application. |
|||
|
|||
Symptoms: |
|||
|
|||
- modules reference each other's internals |
|||
- shared entities leak everywhere |
|||
- services depend on concrete implementations across modules |
|||
- repositories are used across boundaries |
|||
|
|||
If that is happening, you have namespaces, not modules. |
|||
|
|||
### 2. A shared project that becomes a dumping ground |
|||
|
|||
Be very careful with anything named: |
|||
|
|||
- Common |
|||
- Shared |
|||
- Core |
|||
- Utilities |
|||
|
|||
Some shared infrastructure is fine. Shared business logic is often a sign that boundaries are unclear. |
|||
|
|||
Prefer: |
|||
|
|||
- duplicated tiny code over premature shared abstractions |
|||
- explicit module contracts over giant common libraries |
|||
|
|||
### 3. Overusing events |
|||
|
|||
Events are powerful, but they can hide the system's real behavior. |
|||
|
|||
If every use case fires multiple events that trigger more events, debugging becomes painful. |
|||
|
|||
Use events deliberately for decoupled reactions, not as a replacement for clear application flows. |
|||
|
|||
### 4. Choosing module boundaries by org chart or UI screens |
|||
|
|||
A screen is not necessarily a module. Neither is a department name. |
|||
|
|||
Choose boundaries based on business capability and ownership of rules and data. |
|||
|
|||
### 5. Ignoring future extraction concerns entirely |
|||
|
|||
You do not need to design for microservices from day one, but you should avoid decisions that make extraction impossible later. |
|||
|
|||
Examples: |
|||
|
|||
- direct table joins across module boundaries |
|||
- exposing internal entities everywhere |
|||
- no public contracts between modules |
|||
|
|||
ABP's modular style helps here, but only if you actually respect it. |
|||
|
|||
## Modular monolith vs microservices in ABP |
|||
|
|||
ABP supports both styles, which makes the comparison especially relevant. |
|||
|
|||
### Choose a modular monolith when |
|||
|
|||
- your team is small to medium-sized |
|||
- you want fast delivery with lower ops cost |
|||
- business boundaries exist, but independent deployment is not yet needed |
|||
- you want a cleaner architecture than a traditional monolith |
|||
|
|||
### Choose microservices when |
|||
|
|||
- modules must be deployed independently |
|||
- scaling characteristics differ sharply by capability |
|||
- organizational ownership is strongly separated |
|||
- you can absorb the cost of distributed systems complexity |
|||
|
|||
### When NOT to use a modular monolith |
|||
|
|||
Do not use it if you already know that: |
|||
|
|||
- teams need full autonomy over deployment cadence |
|||
- strict runtime isolation is required |
|||
- independent data ownership must be enforced operationally from the start |
|||
|
|||
For many products, a modular monolith is the better first architecture because it preserves optionality. You can grow into more distribution later instead of paying for it before you need it. |
|||
|
|||
## A practical path for future extraction |
|||
|
|||
One of the best reasons to build a modular monolith with ABP is that the module shape is already compatible with a more distributed future. |
|||
|
|||
That does not mean extraction is free. It never is. But you can make it realistic. |
|||
|
|||
To keep that option open: |
|||
|
|||
- keep public contracts narrow |
|||
- avoid direct database coupling across modules |
|||
- communicate through application services and events |
|||
- keep module-specific logic inside the module |
|||
- treat each module as owning its own data and rules |
|||
|
|||
If one day Ordering needs to become its own service, the work becomes an architectural transition instead of a rescue mission. |
|||
|
|||
## Recommended approach for a first real project |
|||
|
|||
If I were starting a new ABP Studio solution today, I would keep it practical. |
|||
|
|||
I would: |
|||
|
|||
- create a modular monolith solution in ABP Studio |
|||
- start with 2 to 4 meaningful business modules |
|||
- use layered modules only where the complexity justifies it |
|||
- default to a single database |
|||
- enforce module boundaries in code review |
|||
- use direct service calls first, events second |
|||
- add tests per module from the beginning |
|||
|
|||
I would avoid: |
|||
|
|||
- designing ten modules before shipping one feature |
|||
- building a giant shared library |
|||
- using events for every interaction |
|||
- leaking persistence details across modules |
|||
|
|||
That balance is usually what keeps the architecture alive after the first few sprints. |
|||
|
|||
## TL;DR |
|||
|
|||
- ABP Studio makes modular monolith architecture practical by separating the host app in `main` and business capabilities in `modules`. |
|||
- ABP modules should own their domain, application logic, persistence, APIs, and tests with explicit dependencies. |
|||
- Keep module communication intentional: direct calls for request-response flows, events for decoupled reactions. |
|||
- Start with a single deployment and usually a single database, but protect boundaries as if extraction may happen later. |
|||
- The biggest risk is not the monolith itself; it is weak module boundaries that turn the codebase back into a big ball of mud. |
|||
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 957 KiB |
|
After Width: | Height: | Size: 822 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
@ -0,0 +1,163 @@ |
|||
You already have an IDE. You might already have Aspire. You might already have Cursor. The remaining question is how those tools handle ABP solution structure, modules, run profiles, and generated application code. |
|||
|
|||
Teams compare [ABP Studio](https://abp.io/studio) with three neighbors. This is not an “IDE replacement” debate. |
|||
|
|||
1. .NET Aspire, for describing distributed resources as code and watching them on a dashboard. |
|||
2. Generic AI IDEs (Cursor, GitHub Copilot, Claude Code), for repo-wide chat and file edits. |
|||
3. CRUD scaffolders, such as `dotnet aspnet-codegenerator` and visual Blazor builders (for example Radzen). |
|||
|
|||
Those tools are good. Keep Visual Studio, Rider, or VS Code. Studio can [open the solution in them](https://abp.io/docs/latest/studio/solution-explorer). This article is about what the ABP workflow brings together in one environment: templates, module graph, Solution Runner (Studio’s tool for starting, stopping, and inspecting applications), the ABP Agent, and [ABP Suite](https://abp.io/suite) (including React). |
|||
|
|||
## TL;DR |
|||
|
|||
Choose ABP Studio when the work is ABP: solution shape, module graph, Solution Runner, AI Agent, and Suite CRUD (including React on modern solutions). |
|||
|
|||
Keep Visual Studio, Rider, or VS Code beside it for refactor, tests, and language services. |
|||
|
|||
Add .NET Aspire when you want resource-as-code and the Aspire dashboard. Studio does not forbid it; microservice templates can wire Aspire in. |
|||
|
|||
Use a generic AI IDE as an extra editor if the team already lives there. Prefer the ABP Agent when the change needs modules, migrations, proxies, or live run telemetry. |
|||
|
|||
Tables in this article follow [official ABP documentation](https://abp.io/docs/latest). |
|||
|
|||
## If the bottleneck is ABP, not the C# file |
|||
|
|||
Studio may be a suitable choice when your team is (or will be) on the ABP Platform and you want one place to: |
|||
|
|||
- Scaffold production templates (including React Modern and Classic MVC / Blazor / Angular). |
|||
- Run many services, containers, and UIs from a [Solution Runner](https://abp.io/docs/latest/studio/running-applications) profile (the set of applications and services started together). |
|||
- Ask, plan, and implement with AI against modules, packages, migrations, and live telemetry. |
|||
- Generate layered CRUD with ABP Suite for MVC, Blazor, Angular, and React. |
|||
|
|||
If you only need a general editor, keep using your IDE. If you only need container orchestration and OpenTelemetry locally, Aspire may already be on the machine. Add Studio when the solution graph and ABP conventions are the bottleneck, not the C# file. |
|||
|
|||
## What actually sits next to Studio |
|||
|
|||
| Capability | ABP Studio + Agent | .NET Aspire | General AI IDEs (Cursor, Copilot, Claude Code, …) | Visual Studio / Rider / VS Code | |
|||
|---|---|---|---|---| |
|||
| Role | ABP solution lifecycle + AI coding in context | Cloud-native orchestration, service discovery, dashboard | Repo-wide AI edit / chat / agents | General-purpose IDE | |
|||
| ABP / DDD structure in context | Solution, modules, packages, run profiles in the [agent system context](https://abp.io/docs/latest/studio/ai-agent) | Unaware of ABP modules | Files and text; no ABP module graph | Solution explorer of projects, not ABP modules | |
|||
| ABP-shaped artifacts | Suite generates entity, app service, DTOs, repository, and UI; Agent follows those module conventions | No | Generic C# unless you prompt every ABP convention | Language services only | |
|||
| Production ABP templates | Layered, modular, microservice; Modern React or Classic UIs | Minimal host templates for Aspire | None | `dotnet new` / ABP CLI if you add it | |
|||
| Run + monitor | [Solution Runner](https://abp.io/docs/latest/studio/running-applications): start/stop apps and containers, HTTP requests, exceptions, logs, events, browse UI | Aspire dashboard: resources, logs, traces, metrics | Shell / terminal processes | Multi-project debug | |
|||
| AI that can build, migrate, generate proxies | Agent mode: files, shell, [Studio tools](https://abp.io/docs/latest/studio/ai-agent-built-in-capabilities) (`dotnet_build`, `start_applications`, `generate_csharp_proxies`, `get_exceptions`, …), MCP | Not an ABP coding agent | Shell-wrapped `dotnet` if you teach it | Copilot in the editor (generic) | |
|||
| Kubernetes | Studio Kubernetes integration for ABP solutions | Publish / deploy story for Aspire apps | No | No (unless you add tools) | |
|||
| Code edit / refactor | Agent writes code; Open with your IDE for deep refactor | Not an editor | Strong | Strongest language tooling | |
|||
| Use together? | Yes, open IDE from Studio; optional Aspire integration | Yes with ABP microservice template | Yes as an extra editor | Yes; Studio launches them | |
|||
|
|||
How to read the table: these tools stack. Studio does not replace Rider's refactorings or Aspire's resource model. Its differentiated value is the ABP-aware workflow across solution structure, modules, run profiles, and agent tools. |
|||
|
|||
Kubernetes. Studio’s Kubernetes integration is for developing against a cluster (browse and health using service names from the run profile). It is not Helm, GitOps, or your production deploy pipeline. |
|||
|
|||
## The ABP Agent vs “just ask Cursor” |
|||
|
|||
 |
|||
|
|||
Cursor and Copilot are excellent at finishing a LINQ query or explaining a regex. That is not the argument. |
|||
|
|||
Ask a generic AI IDE: "Add a Product entity, make it multi-tenant, add the EF Core migration, generate the React UI." A generic AI IDE can work effectively when it has sufficient repository context and explicit project conventions. ABP Studio's differentiated value is that solution, module, run-profile, and telemetry context are available through first-class Studio capabilities. For cross-layer ABP changes, this can reduce the amount of context and manual coordination the developer must provide. |
|||
|
|||
[ABP Studio AI Agent](https://abp.io/docs/latest/studio/ai-agent) is not limited to the currently open text buffer. The session can use the solution, modules, packages, runnable apps, run profile, AI scope, and enabled tools. [ABP Suite](https://abp.io/suite) remains the CRUD generator, while the Agent is designed to work with the module graph, add a migration, start the profile, and inspect live exceptions. |
|||
|
|||
Modes: |
|||
|
|||
- Ask, read-only answers. Can search ABP documentation. |
|||
- Plan, read-only implementation plans. |
|||
- Agent, read/write files, shell, add migrations, run Studio tools, MCP, update plan steps. |
|||
|
|||
The ABP Agent fits changes where module, solution, and runtime context should remain available throughout the work. |
|||
|
|||
That is the difference versus a generic AI IDE: |
|||
|
|||
| What you ask | ABP Agent | Generic AI IDE | |
|||
|---|---|---| |
|||
| “Add an app service following ABP layering” | Receives solution, module, and ABP documentation context through Studio | Works from the repository context and instructions provided to it | |
|||
| “Why did this HTTP call fail?” | `get_requests` / `get_exceptions` / `get_logs` on the running profile | You paste a log or attach a debugger | |
|||
| “Generate C# / Angular proxies” | First-class Studio tools | A shell command if the model guesses it | |
|||
| “Start the apps, then continue” | `start_applications` / `start_containers` | Terminal + wait | |
|||
| Scope | AI scopes limit which modules the agent may touch; `.abpignore` blocks secrets | Works from the repository context and instructions provided to it | |
|||
|
|||
You still review the diff. Agent mode is execution with a permission boundary, not unsupervised production deploys. |
|||
|
|||
The [privacy boundary](https://abp.io/docs/latest/studio/ai-agent) is the session you give it: files, prompts, Studio tool output, attachments, and allowed URLs. Privacy depends on the selected model, configured provider, accessible scope, and enabled tools. [`.abpignore`](https://abp.io/docs/latest/studio/ai-agent-configuration) prevents excluded files from entering the agent context. |
|||
|
|||
## Why Aspire does not replace Studio (they stack) |
|||
|
|||
[Solution Runner](https://abp.io/docs/latest/studio/running-applications) is how you run an ABP modular or microservice tree: profiles per team, folders for apps/gateways/services, C# hosts, CLI tasks (for example Angular), Docker containers, start/stop/build, browse, health, and live HTTP / exception / log / event views. |
|||
|
|||
.NET Aspire is how many .NET teams describe distributed resources as code and watch them on a dashboard (OpenTelemetry, containers, connection strings). |
|||
|
|||
Their responsibilities overlap around local orchestration, but their primary scopes are different. Aspire focuses on distributed resource orchestration, while ABP Studio adds ABP solution templates, module workflows, Suite, and the ABP Agent. |
|||
|
|||
You do not have to pick one. ABP microservice templates can enable Aspire so AppHost starts infrastructure and services; you can still use Studio’s runner and Agent. See [Aspire integration](https://abp.io/docs/latest/solution-templates/microservice/aspire-integration). |
|||
|
|||
Pairing ABP Studio with Aspire works well when ABP solution workflows and distributed resource orchestration are both needed. |
|||
|
|||
## Suite: a CRUD slice, not a pretty grid |
|||
|
|||
[ABP Suite](https://abp.io/docs/latest/suite) generates a CRUD slice of an ABP application from an entity: domain type, repository, application service, migration, UI, tests, navigation properties, multi-tenant flag, localization keys. |
|||
|
|||
| | ABP Suite | `dotnet aspnet-codegenerator` / EF scaffolding | Visual Blazor app builders (for example Radzen) | |
|||
|---|---|---|---| |
|||
| Output | Entity through application layer + UI + optional tests | Controllers, Razor Pages, Blazor CRUD, or Minimal API endpoints against a DbContext | Blazor UI + data wiring from a database or REST source | |
|||
| UI stacks | MVC, Blazor (Blazorise or MudBlazor, detected), Angular, and React (modern solutions) | MVC views, Razor Pages, and Blazor components. No Angular. No React. | Blazor only (Server, WebAssembly, Auto) | |
|||
| ABP permissions, tenancy, audit base classes | Options on the entity ([CRUD generation](https://abp.io/docs/latest/suite/generating-crud-page)) | Does not use ABP permission and tenancy conventions | Does not use ABP permission and tenancy conventions | |
|||
| Custom code on regenerate | Customizable code [hook points](https://abp.io/docs/latest/suite/customizing-the-generated-code) for MVC, Blazor, and Angular; React pages require extra care when regenerated | Often requires manually preserving custom changes | Varies by product | |
|||
| React UI | Yes, template-based CRUD for modern React apps, including search, paging, validation, permissions, localization, and navigation properties. Registers routes and menu | No React generator in the official scaffolding set (`blazor`, `razorpage`, `view`, `controller`, `identity`, `minimalapi`) | Blazor-focused rather than React-based | |
|||
|
|||
Suite supports the official web UI stacks, including React. [ABP Agent](https://abp.io/docs/latest/studio/ai-agent) is an extra path when you want AI to evolve those pages, not a substitute for Suite React output. |
|||
|
|||
Suite is a fit when generated CRUD should follow the application's ABP layers, conventions, and selected UI stack. |
|||
|
|||
## When Studio is the right tool in the stack |
|||
|
|||
Choose ABP Studio when the work is ABP: new solution shape, module graph, run profiles, Kubernetes-connected browse, Agent that can migrate and generate proxies, Suite for CRUD. |
|||
|
|||
Keep your IDE open beside it for refactor, tests, and language services. Studio expects that. |
|||
|
|||
Add Aspire when you want resource-as-code and the Aspire dashboard. Studio does not forbid it. |
|||
|
|||
Use a generic AI IDE as an extra editor if your team already lives there. Prefer Agent for ABP-structured changes so the model is not guessing module boundaries from filenames. |
|||
|
|||
Studio works best when ABP-specific solution workflows complement your existing IDE. |
|||
|
|||
## FAQ |
|||
|
|||
### We already run .NET Aspire (AppHost + dashboard). What does Studio still do that Aspire does not? |
|||
Aspire models resources (projects, containers, connection strings, OpenTelemetry) as code. Studio models an ABP solution: modules, package installation, production templates, Suite, Kubernetes browse for ABP services, and an agent that can migrate and generate proxies. Their responsibilities overlap around local orchestration, but their primary scopes are different. You can keep AppHost; microservice templates can integrate Aspire. You do not need to replace Aspire with Studio. |
|||
|
|||
### If the team already uses Cursor or Copilot on the same repo, when is ABP Agent the better tool for a task? |
|||
When the task needs ABP structure or a live run, not only a file edit: module/package scope, `generate_csharp_proxies` / `generate_angular_proxies`, `start_applications`, or `get_exceptions` / `get_requests` / `get_logs` against the [Solution Runner](https://abp.io/docs/latest/studio/running-applications) profile. Generic IDEs can work in the same repository when given the relevant context and conventions. Agent provides first-class access to module, run-profile, and Studio tool context for changes that coordinate ABP modules and running services. |
|||
|
|||
### After Suite generates React CRUD, how should we customize the page? |
|||
For MVC, Blazor, and Angular, use Suite’s [customizable code](https://abp.io/docs/latest/suite/customizing-the-generated-code) hook points. React pages require extra care when regenerated, so keep additional React UI in files Suite does not generate or evolve it with [ABP Agent](https://abp.io/docs/latest/studio/ai-agent). Suite React generation itself is template-based, not AI. |
|||
|
|||
### Can Agent start the microservice profile, then use real HTTP/exception data in the same session? |
|||
Yes, in Agent mode with a run profile: tools include `start_applications` / `start_containers` and then `get_requests`, `get_exceptions`, `get_logs`, `get_events` ([built-in capabilities](https://abp.io/docs/latest/studio/ai-agent-built-in-capabilities)). Ask/Plan cannot mutate or start apps. If nothing is running, those telemetry tools have nothing to read. That is why “AI that sees production-like local traffic” is a Studio and Runner loop, not a chat sidebar. |
|||
|
|||
### Does Studio Kubernetes integration replace Helm, GitOps, or our cluster deploy pipeline? |
|||
No. It is for developing against a cluster (browse/health using Kubernetes service names from the run profile, manage connected services). Aspire’s publish story and your CI remain how you ship. Do not treat the Studio K8s panel as the production deployment product. |
|||
|
|||
### Can one developer live in Rider and another in VS Code while sharing the same Studio solution? |
|||
Yes. Studio holds solution/run-profile metadata; [Open with](https://abp.io/docs/latest/studio/solution-explorer) launches whatever IDE is installed. Run profiles and Agent sessions are not tied to a single editor vendor. The constraint is ABP Studio itself on the machine, not a mandate to abandon Rider or VS. |
|||
|
|||
## Next step |
|||
|
|||
[Download ABP Studio](https://abp.io/studio) · [AI Agent docs](https://abp.io/docs/latest/studio/ai-agent) · [Generate a CRUD page](https://abp.io/docs/latest/suite/generating-crud-page) · [ABP Framework](https://abp.io/framework) |
|||
|
|||
### Sources |
|||
|
|||
- [ABP Studio overview](https://abp.io/docs/latest/studio) |
|||
- [AI Agent](https://abp.io/docs/latest/studio/ai-agent) |
|||
- [AI Agent built-in capabilities](https://abp.io/docs/latest/studio/ai-agent-built-in-capabilities) |
|||
- [Solution Runner](https://abp.io/docs/latest/studio/running-applications) |
|||
- [Solution Explorer / Open with IDE](https://abp.io/docs/latest/studio/solution-explorer) |
|||
- [ABP Suite](https://abp.io/docs/latest/suite) |
|||
- [Generating a CRUD page](https://abp.io/docs/latest/suite/generating-crud-page) |
|||
- [Customizing the generated code](https://abp.io/docs/latest/suite/customizing-the-generated-code) |
|||
- [ASP.NET Core `aspnet-codegenerator`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/tools/dotnet-aspnet-codegenerator) |
|||
- [.NET Aspire overview](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview) |
|||
- [Radzen Blazor Studio](https://www.radzen.com/blazor-studio/) |
|||
- [Microservice Aspire integration](https://abp.io/docs/latest/solution-templates/microservice/aspire-integration) |
|||
|
|||
Product names in this article belong to their owners. Mention is for identification in a technical comparison, not affiliation or endorsement. |
|||
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
@ -0,0 +1,223 @@ |
|||
Every serious .NET team hits the same whiteboard sooner or later. Do we build the multi-tenant SaaS from scratch? Grab a starter kit? Adopt a platform? |
|||
|
|||
Search “ABP vs …” and you will land on pages written by kits and tenancy libraries. Fair. Those pages exist because the question is real. They are not comparing two products of the same kind. |
|||
|
|||
You are choosing among four shapes: |
|||
|
|||
1. Build it yourself on ASP.NET Core, often with a tenancy library such as Finbuckle.MultiTenant. |
|||
2. A Clean Architecture template (Jason Taylor, Ardalis) that gives you folders, layers, and a sample feature. |
|||
3. A starter kit that copies a snapshot of production modules into your repo (fullstackhero, Brick, and similar kits). |
|||
4. A maintained application platform. Architecture, infrastructure, modules, and tools that keep moving. |
|||
|
|||
ABP competes across several decision categories rather than against one uniform product set. Some teams compare it with Clean Architecture and DDD references or starter kits because they prefer to own and assemble the application foundation themselves. Others evaluate .NET application and service frameworks such as ServiceStack, model-driven RAD platforms such as DevExpress XAF, or cross-stack SaaS platforms and starter kits. Existing ASP.NET Boilerplate applications are usually a migration and adoption consideration rather than a same-generation alternative. The table below focuses on the architecture and application-foundation decision; these other categories can still enter the same broader technology discussion. |
|||
|
|||
[ABP](https://abp.io) takes the application-platform approach. The rest of this article is about the things teams evaluate later: multi-tenancy, identity, permissions, modularity, UI (including React), and a path from modular monolith (one deployable application organized into independent modules) to microservices. |
|||
|
|||
## TL;DR |
|||
|
|||
Choose ABP when you are shipping a long-lived, multi-tenant, modular .NET product and want a maintained platform: tenancy, identity, modules, official UIs (including React), and Studio / Suite on the same stack. |
|||
|
|||
Choose a Clean Architecture template (Jason Taylor, Ardalis) when the goal is to own every architectural decision from a well-named skeleton. |
|||
|
|||
Choose a starter kit (fullstackhero, Brick, and similar) when you prefer a copy of modules in your repo on day one and you are willing to maintain that snapshot. |
|||
|
|||
Choose Finbuckle (or custom tenant middleware) when tenant resolution is the only additional capability you need on an app you already own. ABP also connects tenancy with application concerns such as identity, permissions, and audit. |
|||
|
|||
Tables in this article follow [official ABP documentation](https://abp.io/docs/latest). |
|||
|
|||
## This is for the long haul |
|||
|
|||
ABP is the recommended choice when you are building a long-lived, multi-tenant, modular .NET product. B2B SaaS. Line of business. A platform several teams will still be extending in a few years. |
|||
|
|||
You want: |
|||
|
|||
- Cross-cutting concerns (auth, tenancy, audit, jobs, localization) already solved and documented. |
|||
- Official UI options that match your team: React, Angular, Blazor, MVC, MAUI, or React Native. |
|||
- A way to add pre-built modules and, later, split a module into a service without rewriting the application code. |
|||
- Tooling (Studio, Suite, CLI) and, if you need it, commercial modules and support, without leaving the same platform. |
|||
|
|||
Yes, ABP is opinionated. The initial learning cost is real: teams need to understand modules, application services, and unit of work conventions before those conventions become productive. Then, when a module needs to become a microservice, the contracts are already there. The conventions become team-wide standards so the next project does not invent a second way to do the same thing. |
|||
|
|||
## Four shapes. One table. |
|||
|
|||
 |
|||
|
|||
| Capability | ABP Platform | DIY ASP.NET Core | Clean Architecture templates (Jason Taylor, Ardalis) | Starter kits (fullstackhero, Brick, and similar) | |
|||
|---|---|---|---|---| |
|||
| What you get | Maintained framework + modules + templates + tooling | Empty host and libraries you assemble | Layered/CQRS skeleton + a sample feature | A copy of modules in your repo on day one | |
|||
| Multi-tenancy | Native: [single database, database-per-tenant, or hybrid](https://abp.io/docs/latest/framework/architecture/multi-tenancy); identity and authentication/token infrastructure wired for tenants | You design isolation, filters, and cache keys, or add a library such as Finbuckle.MultiTenant | Not included | Often via a tenancy library you still own end-to-end | |
|||
| Identity, permissions, audit | Application modules + automatic tenant-data filtering, [audit logging](https://abp.io/docs/latest/framework/infrastructure/audit-logging), permission system | You compose Identity, policies, and audit yourself | Identity sample at most | Bundled in the kit; you maintain the fork | |
|||
| Modularity | First-class [module system](https://abp.io/docs/latest/framework/architecture/modularity/basics); install/uninstall packages | You invent module boundaries | Folders and layers, not a product module catalog | Modular folders; upgrades are merge/cherry-pick | |
|||
| UI | [React (Modern)](https://abp.io/docs/latest/framework/ui/react), MVC, Blazor, Angular, [React Native](https://abp.io/docs/latest/framework/ui/react-native), [MAUI](https://abp.io/docs/latest/framework/ui/maui) | You pick and integrate | Template-specific (often Angular/React/API) | Kit-specific (often one SPA) | |
|||
| Monolith → microservices | Same application contracts; HTTP/C# proxies replace in-process calls | You design the split | You design the split | Extract-a-module if the kit allows it | |
|||
| Tooling | [ABP Studio](https://abp.io/studio) (including AI Agent), [ABP Suite](https://abp.io/suite), CLI | `dotnet` CLI and your IDE | Template CLI + IDE | Kit CLI / Aspire host, varies | |
|||
| How it evolves | NuGet upgrades for the framework and modules, plus documented migrations | You upgrade every library | You copy the next template over your tree | You merge upstream into source you already customized | |
|||
| Support | Open-source core plus commercial extras and vendor support | Your team | Community | Community | |
|||
|
|||
ABP may be a suitable fit when the application needs architecture, infrastructure modules, and development tooling to remain integrated and maintainable as the product grows. |
|||
|
|||
How to read the table: templates teach structure. Starter kits give you a snapshot you own. ABP combines application architecture, infrastructure modules, and development tooling in one platform. Names in the headers are examples of the category, not a review of every repository. |
|||
|
|||
Identity, permissions, and audit. ABP ships these as application modules: permission UI, automatic filtering of tenant data, and audit logging. On plain ASP.NET Core you compose Identity, policies, and audit yourself. A Clean Architecture template usually stops at an Identity sample. A starter kit may bundle them; you then maintain that copy. |
|||
|
|||
How the stack evolves. When a customized starter kit is updated, identity and tenancy code may already contain local changes. Reconciling those changes across the files you now own can add significant maintenance work. That is a different upgrade model from updating framework packages. |
|||
|
|||
ABP uses framework and module packages, together with documented migrations. A Clean Architecture template’s next version is something you copy forward. A kit’s next version is reconciled with the snapshot you already changed. |
|||
|
|||
## Let’s be honest: what are you actually downloading? |
|||
|
|||
### What you build yourself |
|||
|
|||
ASP.NET Core is phenomenal. It is a foundation, not a complete application platform. Building from scratch gives teams maximum control, but also leaves tenant resolution, permission checks, audit logs, job plumbing, and module boundaries to the team. |
|||
|
|||
That work is legitimate if the architecture is unique. For a standard business product, it is infrastructure before the first domain feature. |
|||
|
|||
ABP sits on ASP.NET Core. You keep the Microsoft stack. ABP provides established conventions and modules for teams that prefer those decisions to be part of the platform. See [Why ABP Platform](https://abp.io/docs/latest/others/why-abp-platform). |
|||
|
|||
ABP suits teams that want application infrastructure to remain integrated with the product as it grows. |
|||
|
|||
### Clean Architecture templates |
|||
|
|||
This category includes the templates most often compared with ABP: Jason Taylor’s Clean Architecture template and Ardalis’s Clean Architecture template. They provide a good classroom and a clean slate. Their focus is architectural structure and ownership rather than a built-in application module catalog or ABP-specific tooling. |
|||
|
|||
Choose a Clean Architecture template when architectural ownership and a focused starting point matter most. |
|||
|
|||
ABP is a fit when architectural conventions and application modules need to evolve together. |
|||
|
|||
### Starter kits |
|||
|
|||
A starter kit copies identity, tenancy, auditing, and a few domain modules into your repository. You own every line. That is a real preference for some teams. |
|||
|
|||
Starter kits are another common option in this decision. fullstackhero is a copy-and-own MIT kit (identity, tenancy, React admin, modules in your repo). Other starter kits, such as Brick, can also provide a prebuilt starting point; the exact features, ownership model, and update process depend on the kit. They can provide a fast starting point, but long-term maintenance depends on how each kit handles updates to customized code. |
|||
|
|||
With ABP, framework and module updates are distributed through packages and documented migrations. With a kit, teams have direct ownership of the source, while updates may require reconciling local changes with upstream code. These are different maintenance models. |
|||
|
|||
Choose a starter kit when an existing set of application features and direct source ownership can accelerate your team. |
|||
|
|||
Choose ABP when you want those application concerns, modules, and tooling to evolve as part of a maintained platform. ABP’s open-source core is yours to use; your business code lives in your repo. There is no separate proprietary runtime for that core. Commercial extras (themes, Pro modules, Suite, support) are optional layers on the same platform, not a rewrite. |
|||
|
|||
Package-based framework and module updates may suit teams that prefer them to reconciling changes in an owned source snapshot. |
|||
|
|||
## If you last looked a year ago: React is official |
|||
|
|||
This is easy to miss if you last looked at ABP a year ago. |
|||
|
|||
Official UI options ([ABP UI](https://abp.io/docs/latest/framework/ui)): |
|||
|
|||
- React, in the [Modern template system](https://abp.io/docs/latest/solution-templates/modern-vs-classic) (ABP Studio or `abp new --modern`). |
|||
- MVC / Razor Pages, Blazor (WebAssembly, Server, WebApp), and Angular, on Classic templates. |
|||
- React Native and MAUI for mobile / hybrid. |
|||
|
|||
[Modern vs Classic](https://abp.io/docs/latest/solution-templates/modern-vs-classic): Classic is not a deprecated track. It is the actively supported family with the broadest UI matrix (MVC, Angular, Blazor, MAUI) and the template-first Studio flow. Modern is the newer architecture-first Studio flow, React (or no UI), and the Admin Console / Low-Code path (metadata-driven screens configured through a designer). Choose Classic when the team’s UI is MVC, Angular, or Blazor. Choose Modern when the web UI is React. |
|||
|
|||
One backend, the UI your team already knows. |
|||
|
|||
## Multi-tenancy without a side project |
|||
|
|||
ABP treats tenancy as infrastructure, not a feature you bolt on: |
|||
|
|||
- Current tenant is resolved per request. |
|||
- Entities implementing `IMultiTenant` (ABP’s contract for separating tenant-owned data) are automatically filtered; new records get a `TenantId` identifying the tenant. |
|||
- You can use one database for all tenants, one database per tenant, or a hybrid. |
|||
|
|||
That isolation also applies to cache and related concerns so business code stays mostly tenancy-agnostic. Details: [Multi-Tenancy](https://abp.io/docs/latest/framework/architecture/multi-tenancy). |
|||
|
|||
A tenancy library on raw ASP.NET Core, including Finbuckle.MultiTenant, can resolve the tenant. It does not automatically give you permission UI, audit, jobs, Identity/OpenIddict authentication and token wiring for tenants, and module installs on the same model. That is the platform difference. Pages titled “Finbuckle vs ABP vs custom” are answering a library question. This article is answering the application-platform question. |
|||
|
|||
ABP is worth considering when tenancy needs to stay connected to identity, permissions, data filtering, and the rest of the application model. |
|||
|
|||
## Modularity that can become microservices |
|||
|
|||
ABP modules are real packages (domain, application, HTTP API, UI) with documented dependency rules. You can start with a [modular monolith](https://abp.io/architecture/modular-monolith) and later replace in-process calls with HTTP using the same application service contracts and [client proxies](https://abp.io/docs/latest/framework/api-development/dynamic-csharp-clients) (typed clients that call application services over HTTP). |
|||
|
|||
That path is the point. You do not throw away the monolith to “do microservices”; you change the hosting of a module that already had a boundary. |
|||
|
|||
This approach fits teams that want modular boundaries to support both a modular monolith and a later service split. |
|||
|
|||
## ABP Low-Code: admin screens inside the same app |
|||
|
|||
[ABP Low-Code](https://abp.io/docs/latest/low-code) is a module in your ABP application (Team license or higher). You model entities, pages, forms, permissions, and scripts in the Admin Console. The runtime uses that metadata in the same app, with the same identity, audit, and EF Core model. There is no separate low-code database. |
|||
|
|||
Designer, [Studio AI Agent](https://abp.io/docs/latest/studio/ai-agent), and hand-written C# / Script API land on one model. |
|||
|
|||
The documented runtime UI is React. You can still keep MVC, Razor Pages, Angular, or Blazor as the main UI and host the React Low-Code runtime beside it ([non-React integration](https://abp.io/docs/latest/low-code/non-react-ui-integration)). |
|||
|
|||
Docs still label the system Preview (APIs and designer may change before GA). That is a maturity label, not a missing product. |
|||
|
|||
| | ABP Low-Code | External low-code platform | Hand-written CRUD every time | |
|||
|---|---|---|---| |
|||
| Where it runs | Inside your ABP app | Separate product / runtime | Your repo | |
|||
| Data and identity | Same database and ABP authorization | Often a parallel model | Whatever you build | |
|||
| Extend with code | C#, Script API, Agent | Platform limits | Always code | |
|||
| Screens | Grid, form, calendar, kanban, gallery, dashboard, import/export | Vendor widgets | Custom pages | |
|||
| How you enable it | Studio modern wizard (layered, single-layer, modular monolith + EF Core) or [add to an existing EF Core solution](https://abp.io/docs/latest/low-code/add-to-existing-solution) | Vendor onboarding | Always code | |
|||
|
|||
Studio’s *new-solution wizard step* is omitted for microservice architecture and when MongoDB is selected, because runtime-managed tables use EF Core. Layered and modular-monolith EF Core solutions are the documented enablement path today. |
|||
|
|||
Use Low-Code for the admin CRUD and metadata-driven screens. Keep distinctive product UX in normal ABP UI, including Suite-generated React CRUD on modern solutions. |
|||
|
|||
## When this is the right call |
|||
|
|||
Pick ABP when: |
|||
|
|||
- The product will have tenants, roles, audit, and more than one team. |
|||
- You want React *or* Blazor *or* Angular *or* MVC on a shared backend. |
|||
- You expect to grow from modular monolith to services without a rewrite. |
|||
- You want Studio, Suite, and (optionally) Low-Code on the same stack. |
|||
|
|||
A Clean Architecture template is a solid choice when architectural ownership and a focused starting point matter most. |
|||
|
|||
A starter kit is a solid choice when an existing set of application features and direct source ownership can accelerate your team. ABP is a better fit when those application concerns, modules, and tooling should evolve as part of a maintained platform. |
|||
|
|||
ASP.NET Boilerplate is the predecessor, not a same-generation starter kit. If that is the comparison, it is a migration to the current ABP platform, not a fork-and-own kit decision. |
|||
|
|||
DevExpress XAF and similar commercial frameworks follow a model-driven RAD approach built around a vendor UI ecosystem. That approach can reduce application boilerplate and accelerate internal business applications, while also placing the application’s model and much of its UI experience within DevExpress conventions and controls. ABP is an application platform on ASP.NET Core, with an open-source core, modular architecture, and multiple UI options. It gives teams more direct control over the application layers and frontend choices, including MVC, Blazor, Angular, and React, while supporting a modular-monolith-to-microservices path. XAF may be the better fit when the priority is delivering a model-driven business application quickly within the DevExpress ecosystem. ABP may be the better fit when the product needs ASP.NET Core, a more customized frontend experience, multiple client options, or a modular architecture that can evolve toward services. |
|||
|
|||
## FAQ |
|||
|
|||
### Is ABP free or commercial? |
|||
ABP Framework has an open-source core. Some themes, modules, generators, Studio capabilities, support, and other commercial offerings require an appropriate commercial license. Exact availability depends on the product and license level. |
|||
|
|||
### If we stop paying for commercial extras, or we leave ABP later, is the app a black box? |
|||
No. The application is still ASP.NET Core, your C#, and EF Core (or MongoDB). The [open-source framework](https://abp.io/docs/latest/others/why-abp-platform) stays in the solution as packages you already reference. Leaving ABP means replacing those packages the way you would replace any framework, not extracting code from a closed runtime. Suite output is source in your repo. Commercial modules, themes, and Suite itself are optional layers; dropping them does not delete your domain. |
|||
|
|||
### If we already have a Clean Architecture or starter-kit repo (Jason Taylor, Ardalis, fullstackhero, Brick), can we “just add ABP” like a NuGet? |
|||
No. ABP’s module pipeline, interceptors, automatic tenant-data filters (which limit queries to the current tenant), and startup conventions assume it is the host. Teams adopt ABP on a new solution (or plan a migration), they do not drop `Volo.Abp.*` onto an existing CA template and keep the old composition root unchanged. That is the real cost versus a tenancy library you add to code you already own. See [Why ABP Platform](https://abp.io/docs/latest/others/why-abp-platform). |
|||
|
|||
### We already use Finbuckle (or custom tenant middleware). What does ABP tenancy still do that that library does not? |
|||
A tenancy library resolves *who the tenant is*. ABP also isolates queries and inserts (`IMultiTenant` data filters), wires Identity and OpenIddict for tenants, and applies the same tenant context to cache and related infrastructure. That covers [single database, database-per-tenant, or hybrid](https://abp.io/docs/latest/framework/architecture/multi-tenancy). Permissions, audit, and jobs then run inside that model instead of each being a separate integration project. |
|||
|
|||
### If we start Modern/React, can the same backend later serve a second UI (public MVC, partner Blazor, mobile)? |
|||
Yes. Application services and HTTP APIs are UI-agnostic. [Official UIs](https://abp.io/docs/latest/framework/ui) (React, Angular, Blazor, MVC, React Native, MAUI) are hosts on that backend. You choose Modern vs Classic at solution creation for the primary web app; extra clients consume the same APIs. You do not rewrite domain or application layers to add a second frontend. |
|||
|
|||
### When we split a module into a microservice, do we rewrite application services as controllers? |
|||
Not as the default path. Module contracts stay; in-process calls become HTTP (or messaging) via [client proxies](https://abp.io/docs/latest/framework/api-development/dynamic-csharp-clients) against the same application-service interfaces. You change hosting, not the feature’s application API. That is the comparison point versus a starter kit whose “modules” are folders with no proxy story. |
|||
|
|||
### For the same admin entity, should we use Suite-generated React CRUD or Low-Code pages? |
|||
[Suite](https://abp.io/docs/latest/suite) writes source into your layers (entity, app service, UI, tests) that you commit and customize, including React on modern solutions. [Low-Code](https://abp.io/docs/latest/low-code) keeps the entity in metadata (designer + runtime APIs/pages) without generating those classes for the standard flow. Use Suite when the screen will grow into product code; use Low-Code when the screen should stay designer-driven. They are not substitutes for each other. |
|||
|
|||
### Can we keep MediatR / vertical-slice handlers for new features inside an ABP solution? |
|||
You can reference extra libraries, but ABP’s default application surface is [application services](https://abp.io/docs/latest/framework/architecture/domain-driven-design/application-services) with unit of work, validation, and authorization conventions. Comparison with CA templates is not “MediatR vs nothing”; it is whether the team standardizes on ABP’s application layer or on a handler-per-feature folder as the public API. Mixing both without a rule usually duplicates the same use case in two styles. |
|||
|
|||
## Next step |
|||
|
|||
Create a solution in [ABP Studio](https://abp.io/studio) and pick Modern (React) or Classic (MVC / Blazor / Angular). Then add only the modules you need. |
|||
|
|||
[Create a free ABP solution](https://abp.io/studio) · [Read the framework overview](https://abp.io/framework) · [Why ABP Platform](https://abp.io/docs/latest/others/why-abp-platform) |
|||
|
|||
### Sources |
|||
|
|||
- [ABP UI options](https://abp.io/docs/latest/framework/ui) |
|||
- [Modern vs Classic templates](https://abp.io/docs/latest/solution-templates/modern-vs-classic) |
|||
- [React UI](https://abp.io/docs/latest/framework/ui/react) |
|||
- [Multi-tenancy](https://abp.io/docs/latest/framework/architecture/multi-tenancy) |
|||
- [Why ABP Platform](https://abp.io/docs/latest/others/why-abp-platform) |
|||
- [Low-Code System](https://abp.io/docs/latest/low-code) |
|||
- [Jason Taylor Clean Architecture template](https://github.com/jasontaylordev/CleanArchitecture) |
|||
- [Ardalis Clean Architecture template](https://github.com/ardalis/CleanArchitecture) |
|||
- [fullstackhero starter kit](https://github.com/fullstackhero/dotnet-starter-kit) |
|||
- [Finbuckle.MultiTenant documentation](https://www.finbuckle.com/MultiTenant/Docs) |
|||
- [ServiceStack documentation](https://docs.servicestack.net/why-servicestack) |
|||
- [DevExpress XAF overview](https://www.devexpress.com/products/net/application_framework/) |
|||
|
|||
Product names in this article belong to their owners. Mention is for identification in a technical comparison, not affiliation or endorsement. |
|||
|
After Width: | Height: | Size: 502 KiB |
@ -0,0 +1,177 @@ |
|||
# How to Present Your .NET Project To Your Mother? |
|||
|
|||
**I'm writing this article while camping in the middle of the forest 🌲⛺.** I came here to get away from computers for a while... but apparently, I'm still thinking about software and how we explain it to people. You can see me in the cover image 😄 |
|||
|
|||
 |
|||
|
|||
Hey there! I'm Alper, giving international conference talks and mostly I don't know the audience demography (knowledge level/position/tech stack etc...). And I need to be very careful how I present my talk. Recently I've been studying on this topic. My audience is mostly developers or IT guys. And when I explain a technical topic to those people, I'm very comfortable 🤠🤠 But, sometimes we need to explain what we do (our project) to our friends, mother or child. In other words; anyone who is not technical. Then things are getting a little bit different and harder 🤯 |
|||
|
|||
To have a strong technical knowledge does not automatically make someone a good communicator. |
|||
For scientists, engineers, developers and technical leaders, the real challenge is not knowing the subject 👉 **deciding what the audience needs to know and how to make it meaningful for them.** |
|||
|
|||
Empathy is very important in all our life steps. I wrote an article about [empathy in workspace](https://abp.io/community/articles/empathy-in-the-workplace-for-software-companies-wsjjw9we), please read it when you have time because it's also important for this topic. Imagine you are explaining your .NET project to an investor or customer, concepts which you use in your project might be simple for you, but feel that empathy (please) for a moment, **does that guy understand what you mean**? Before starting your presentation collect information about him so that you can adjust your sentences according to him. |
|||
|
|||
> AND TELL HIM ONLY WHAT WORKS FOR HIM. |
|||
|
|||
No need to tell how the algorithm behind this project works! Tell him why it's good for him. From now on, when I tell "him/his" I mean the customer / investor / friend / father / son / non-IT guy. |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
## Don't Focus Too Much on the Details |
|||
|
|||
We are all good software engineers and we want to show the art behind that work 👏 That's absolutely natural feeling 👍 |
|||
We all want to show people the details how our project work because we are proud of it and that's a bad practise!! |
|||
Too much detail will definitely hide your main message!!! You are consuming his energy, focus and time with all those details. |
|||
So what happens, your main goal is not being transferred to him 🤔..💭...🥺 |
|||
|
|||
 |
|||
|
|||
Ok we know the problem, now how will you fix this? SIIMMPLEE: Before mentioning a topic, ask yourself: |
|||
|
|||
> Does he need this info to understand my idea or make his decision? |
|||
|
|||
If not, please save it for yourself and don't mention about that. |
|||
Maybe later, if he's very much interested in you can explain him (IF ONLY he asks 😄) |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
## Don't Make It Too Simple Also *-- are you kidding me!* |
|||
|
|||
Well! Don't be angry 😡 at me. In the previous section I said *don't go in details* and now I'm telling **do not explain too simple** 🙃 |
|||
While we are trying to not make it too complicated or detailed, we shouldn't make it inaccurate or treating the listener as idiot🐑 |
|||
|
|||
Keep the science and technical meaning balanced. Let's reduce unnecessary software jargons. Use clear language, examples and familiar concepts when you explain something complex. |
|||
**My technique is using analogies**, especially for my projects I often give a car example which is as simple as everybody can easily understand (*even my kid understands it*). |
|||
|
|||
> MAKE IT SIMPLE, but NOT OVER-SIMPLE! |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
## Start With Why It Matters |
|||
|
|||
In Turkey, there are some street sellers... They stop you and say "Can I tell you something?". Actually I know he's a seller and probably he'll redirect me to a barber, cosmetic store or some shops which are upper floors in a passage. And I mostly say "NO!" and I'm walking my way... But imagine if he tells me first which benefit I'll get from what he sells, maybe I'll go to his shop. (*this paragraph is also explaining my topic in a simple way which I explained in the previous section so I'm still using these techniques in this article* 🤫) |
|||
|
|||
 |
|||
|
|||
One of the most common mistakes in technical presentations is starting with the technology itself. |
|||
|
|||
> Our project runs the Dijkstra algorithm with the multi-tenant and DDD architecture 🧑🔬 |
|||
|
|||
The above sentence doesn't mean anything to him. Tell it like this: |
|||
|
|||
> Our project finds the shortest path efficiently, you can have many isolated customers with clean design and long years maintainable way.” |
|||
|
|||
When people understand why your project matters, they'll have a reason to listen the next sections. |
|||
|
|||
So far, so good 💯 You did a good job reading until here 🙏 ᵗʰᵃᶰᵏᵧₒᵤ ... Keep reading please ...👀...👇 |
|||
|
|||
|
|||
--- |
|||
|
|||
## Think About What You Want Him to Remember |
|||
|
|||
Your audience will not remember many technical points after your presentation. |
|||
**They will remember 1-2 topics.** |
|||
Decide what those points are 🎯 then build the presentation around them. |
|||
Everything else must support those core messages. |
|||
|
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
## Your Voice Tone Matters Too! |
|||
|
|||
Imagine you did everything 👏 very good until here... but you are talking very monotonous way. Or you are talking quickly. Sorry, that's also not a good talk! **Your emphasis, eye contact, body language, tone of voice are all important.** You can make small jokes to give him a relaxing break to understand your complex technical work. |
|||
|
|||
> COMMUNICATION IS NOT ONLY ABOUT WHAT YOU SAY but also HOW YOU SAY IT 🗣 |
|||
|
|||
Watch the📹 [ShadeZahrai -very short- tiktok video](https://www.tiktok.com/@shadezahrai/video/7177549216464571650) |
|||
|
|||
 |
|||
|
|||
|
|||
--- |
|||
|
|||
## Use Images to Make Your Subject Clear |
|||
|
|||
Especially when you are presenting on slides, you should show some funny images related to your topic or you can show analogic images that explains the problem. |
|||
For example to tell about a risky case which you covered in your project, you can use the below image :) |
|||
Yes, people will laugh at first but later they'll listen to you more carefully because you took their attention. (*I created another paradox here, I’m explaining this topic using a funny related image and I know you’re now reading my article more carefully*🤝) |
|||
|
|||
 |
|||
|
|||
|
|||
--- |
|||
|
|||
|
|||
|
|||
## How to Handle Questions? 😡 Especially Which You Don't Like |
|||
|
|||
 |
|||
|
|||
In technical presentations, people can ask aggressive or stupid questions or you may not like their questions 😡 |
|||
Don't immediately try to defend yourself by giving more and more technical details. Or don't try to show them it's a silly question. |
|||
If you don't know, tell it honestly. If that feature doesn't exist, tell that you took note and you'll evaluate it later. |
|||
Even if it’s a stupid question, don’t shut him down. **Later, he will remember only that moment and all your efforts you’ve made will be lost.** |
|||
|
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
## Your Goal Is To Be Understandable 🫱🏽🫲🏻 Not Showing Everything You Know |
|||
|
|||
Anddd again, we come back to empathy! |
|||
|
|||
> The best technical explainer is not the person who gives the most information. |
|||
|
|||
 |
|||
|
|||
Okay, you know a lot and you want to show that. But try to be empathetic again. Giving someone all the technical details doesn't mean they will understand your point. **Good communication means sharing what matters in a way people can easily understand.** |
|||
|
|||
> Don't tell people everything you know. Tell them what they need to know and please make sure they understand it. |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
## Explaining Something and Convincing Someone Are Not the Same 🙄 |
|||
|
|||
Providing accurate & correct information and persuading someone to take action are different. |
|||
But in business life, **technical presentations often need to do both**. |
|||
You need to explain how something works and also helping management approve a project. |
|||
|
|||
YOU CAN USE THIS PATH WHEN PRESENTING YOUR PROJECT: |
|||
|
|||
 |
|||
|
|||
--- |
|||
|
|||
I appreciate you taking the time to read this article 🙏. |
|||
I like to share what I learn which is the main reason I wrote this article. |
|||
By applying these techniques, we can become better presenters and communicate our ideas more effectively 👌. |
|||
When we combine our technical strengths with strong soft skills, I believe we can make a greater impact, inspire others and grow together 🙌💪. |
|||
|
|||
|
|||
|
|||
--- |
|||
|
|||
Alper Ebicoglu |
|||
|
|||
Software architect since the early 2000s. |
|||
Learning, inspiring, sharing, talking... |
|||
|
|||
||| |
|||
| ------------------------------------------------------------ | ------------------------------------------------------------ | |
|||
| <a href="https://www.linkedin.com/in/ebicoglu/"><img src="https://img.icons8.com/color/32/linkedin.png"/></a> | [linkedin.com/in/ebicoglu](https://www.linkedin.com/in/ebicoglu/) | |
|||
| <a href="https://x.com/alperebicoglu"><img src="https://img.icons8.com/color/32/twitterx.png"/></a> | [x.com/alperebicoglu](https://x.com/alperebicoglu) | |
|||
| <a href="https://alperonline.medium.com"><img src="https://img.icons8.com/color/32/medium.png"/></a> | [alperonline.medium.com](https://alperonline.medium.com) | |
|||
| <a href="https://github.com/ebicoglu"><img src="https://img.icons8.com/color/32/github.png"/></a> | [github.com/ebicoglu](https://github.com/ebicoglu) | |
|||
|
|||
|
After Width: | Height: | Size: 368 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 540 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 2.7 MiB |
@ -0,0 +1,13 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"GivenTenantIsNotExist": "지정된 테넌트가 존재하지 않습니다: {0}", |
|||
"GivenTenantIsNotAvailable": "지정된 테넌트를 사용할 수 없습니다: {0}", |
|||
"Tenant": "테넌트", |
|||
"Switch": "전환", |
|||
"Name": "이름", |
|||
"SwitchTenantHint": "호스트 측으로 전환하려면 이름 필드를 비워 두세요.", |
|||
"SwitchTenant": "테넌트 전환", |
|||
"NotSelected": "선택되지 않음" |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar; |
|||
|
|||
public class MalihuCustomScrollbarPluginScriptBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js"); |
|||
} |
|||
} |
|||
@ -1,12 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar; |
|||
|
|||
public class MalihuCustomScrollbarPluginStyleBundleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.css"); |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Packages.JQuery; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.OwlCarousel; |
|||
|
|||
[DependsOn(typeof(JQueryScriptContributor))] |
|||
public class OwlCarouselScriptContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/owl.carousel/owl.carousel.min.js"); |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.OwlCarousel; |
|||
|
|||
public class OwlCarouselStyleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
//TODO: Theming!
|
|||
context.Files.AddIfNotContains("/libs/owl.carousel/assets/owl.carousel.min.css"); |
|||
context.Files.AddIfNotContains("/libs/owl.carousel/assets/owl.theme.default.min.css"); |
|||
context.Files.AddIfNotContains("/libs/owl.carousel/assets/owl.theme.green.min.css"); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Swiper; |
|||
|
|||
public class SwiperScriptContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/swiper/swiper-bundle.min.js"); |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Swiper; |
|||
|
|||
public class SwiperStyleContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/swiper/swiper-bundle.min.css"); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"Volo.Authorization:010001": "권한 부여에 실패했습니다! 지정된 정책에서 권한이 부여되지 않았습니다.", |
|||
"Volo.Authorization:010002": "권한 부여에 실패했습니다! 지정된 정책에서 권한이 부여되지 않았습니다: {PolicyName}", |
|||
"Volo.Authorization:010003": "권한 부여에 실패했습니다! 지정된 리소스에 대해 정책에서 권한이 부여되지 않았습니다: {ResourceName}", |
|||
"Volo.Authorization:010004": "권한 부여에 실패했습니다! 지정된 리소스에 대해 요구 사항이 충족되지 않았습니다: {ResourceName}", |
|||
"Volo.Authorization:010005": "권한 부여에 실패했습니다! 지정된 리소스에 대해 요구 사항들이 충족되지 않았습니다: {ResourceName}" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"MaxResultCountExceededExceptionMessage": "{0}은(는) {1}보다 클 수 없습니다! 더 많은 결과를 허용하려면 서버 측에서 {2}.{3}을(를) 늘리십시오." |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"DisplayName:Abp.Mailing.DefaultFromAddress": "기본 발신자 주소", |
|||
"DisplayName:Abp.Mailing.DefaultFromDisplayName": "기본 발신자 표시 이름", |
|||
"DisplayName:Abp.Mailing.Smtp.Host": "호스트", |
|||
"DisplayName:Abp.Mailing.Smtp.Port": "포트", |
|||
"DisplayName:Abp.Mailing.Smtp.UserName": "사용자 이름", |
|||
"DisplayName:Abp.Mailing.Smtp.Password": "비밀번호", |
|||
"DisplayName:Abp.Mailing.Smtp.Domain": "도메인", |
|||
"DisplayName:Abp.Mailing.Smtp.EnableSsl": "SSL 사용", |
|||
"DisplayName:Abp.Mailing.Smtp.UseDefaultCredentials": "기본 자격 증명 사용", |
|||
"Description:Abp.Mailing.DefaultFromAddress": "기본 발신자 주소입니다.", |
|||
"Description:Abp.Mailing.DefaultFromDisplayName": "기본 발신자 표시 이름입니다.", |
|||
"Description:Abp.Mailing.Smtp.Host": "SMTP 통신에 사용되는 호스트 이름 또는 IP 주소입니다.", |
|||
"Description:Abp.Mailing.Smtp.Port": "SMTP 통신에 사용되는 포트입니다.", |
|||
"Description:Abp.Mailing.Smtp.UserName": "자격 증명에 연결된 사용자 이름입니다.", |
|||
"Description:Abp.Mailing.Smtp.Password": "자격 증명에 연결된 사용자 이름의 비밀번호입니다.", |
|||
"Description:Abp.Mailing.Smtp.Domain": "자격 증명을 인증하는 도메인 또는 컴퓨터 이름입니다.", |
|||
"Description:Abp.Mailing.Smtp.EnableSsl": "SmtpClient가 SSL(Secure Sockets Layer)을 사용하여 연결을 암호화할지 여부입니다.", |
|||
"Description:Abp.Mailing.Smtp.UseDefaultCredentials": "요청과 함께 DefaultCredentials를 전송할지 여부입니다.", |
|||
"TextTemplate:StandardEmailTemplates.Layout": "기본 이메일 레이아웃 템플릿", |
|||
"TextTemplate:StandardEmailTemplates.Message": "이메일용 단순 메시지 템플릿" |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"InternalServerErrorMessage": "요청을 처리하는 동안 내부 오류가 발생했습니다!", |
|||
"ValidationErrorMessage": "요청이 유효하지 않습니다!", |
|||
"ValidationNarrativeErrorMessageTitle": "유효성 검사 중 다음 오류가 발견되었습니다.", |
|||
"DefaultErrorMessage": "오류가 발생했습니다!", |
|||
"DefaultErrorMessageDetail": "서버에서 오류 세부 정보를 보내지 않았습니다.", |
|||
"DefaultErrorMessage401": "인증되지 않았습니다!", |
|||
"DefaultErrorMessage401Detail": "이 작업을 수행하려면 로그인해야 합니다.", |
|||
"DefaultErrorMessage403": "권한이 없습니다!", |
|||
"DefaultErrorMessage403Detail": "이 작업을 수행할 권한이 없습니다!", |
|||
"DefaultErrorMessage404": "리소스를 찾을 수 없습니다!", |
|||
"DefaultErrorMessage404Detail": "서버에서 요청한 리소스를 찾을 수 없습니다!", |
|||
"EntityNotFoundErrorMessage": "{0} 엔터티 중 ID가 {1}인 항목이 없습니다!", |
|||
"EntityNotFoundErrorMessageWithoutId": "{0} 엔터티가 없습니다!", |
|||
"AbpDbConcurrencyErrorMessage": "제출한 데이터가 이미 다른 사용자에 의해 변경되었습니다. 변경 사항을 취소하고 다시 시도해 주세요.", |
|||
"Error": "오류", |
|||
"UnhandledException": "처리되지 않은 예외가 발생했습니다!", |
|||
"Authorizing": "권한을 확인하는 중…", |
|||
"401Message": "인증되지 않음", |
|||
"403Message": "접근 금지", |
|||
"404Message": "페이지를 찾을 수 없음", |
|||
"500Message": "내부 서버 오류", |
|||
"403MessageDetail": "이 작업을 수행할 권한이 없습니다!", |
|||
"404MessageDetail": "죄송합니다. 이 주소에 해당하는 페이지가 없습니다.", |
|||
"Unauthorized": "인증되지 않음", |
|||
"invalid_token": "유효하지 않은 토큰", |
|||
"SessionExpired": "세션이 만료되었습니다. 애플리케이션을 계속 사용하려면 다시 로그인해 주세요." |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"Volo.Feature:010001": "기능이 활성화되지 않았습니다: {FeatureName}", |
|||
"Volo.Feature:010002": "필수 기능이 활성화되지 않았습니다. 다음 기능을 모두 활성화해야 합니다: {FeatureNames}", |
|||
"Volo.Feature:010003": "필수 기능이 활성화되지 않았습니다. 다음 기능 중 하나 이상을 활성화해야 합니다: {FeatureNames}" |
|||
} |
|||
} |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"culture": "ko", |
|||
"texts": { |
|||
"Volo.GlobalFeature:010001": "'{ServiceName}' 서비스에서 '{GlobalFeatureName}' 기능을 활성화해야 합니다." |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
[assembly: InternalsVisibleTo("Volo.Abp.Http.FluentValidation.Tests")] |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
"role": "lib.framework" |
|||
} |
|||
@ -0,0 +1,68 @@ |
|||
{ |
|||
"name": "Volo.Abp.Http.FluentValidation", |
|||
"hash": "", |
|||
"contents": [ |
|||
{ |
|||
"namespace": "Volo.Abp.Http.FluentValidation", |
|||
"dependsOnModules": [ |
|||
{ |
|||
"declaringAssemblyName": "Volo.Abp.Http", |
|||
"namespace": "Volo.Abp.Http", |
|||
"name": "AbpHttpModule" |
|||
}, |
|||
{ |
|||
"declaringAssemblyName": "Volo.Abp.FluentValidation", |
|||
"namespace": "Volo.Abp.FluentValidation", |
|||
"name": "AbpFluentValidationModule" |
|||
} |
|||
], |
|||
"implementingInterfaces": [ |
|||
{ |
|||
"name": "IAbpModule", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IAbpModule" |
|||
}, |
|||
{ |
|||
"name": "IOnPreApplicationInitialization", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IOnPreApplicationInitialization" |
|||
}, |
|||
{ |
|||
"name": "IOnApplicationInitialization", |
|||
"namespace": "Volo.Abp", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.IOnApplicationInitialization" |
|||
}, |
|||
{ |
|||
"name": "IOnPostApplicationInitialization", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IOnPostApplicationInitialization" |
|||
}, |
|||
{ |
|||
"name": "IOnApplicationShutdown", |
|||
"namespace": "Volo.Abp", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.IOnApplicationShutdown" |
|||
}, |
|||
{ |
|||
"name": "IPreConfigureServices", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IPreConfigureServices" |
|||
}, |
|||
{ |
|||
"name": "IPostConfigureServices", |
|||
"namespace": "Volo.Abp.Modularity", |
|||
"declaringAssemblyName": "Volo.Abp.Core", |
|||
"fullName": "Volo.Abp.Modularity.IPostConfigureServices" |
|||
} |
|||
], |
|||
"contentType": "abpModule", |
|||
"name": "AbpHttpFluentValidationModule", |
|||
"summary": null |
|||
} |
|||
] |
|||
} |
|||