microsoft/teams.net
Publicmirrored fromhttps://github.com/microsoft/teams.netAvailable
core/docs/Core-Compat-PackageDependencies.md
227lines · modecode
| 1 | # Package Dependencies Design Document |
| 2 | |
| 3 | This document describes the package dependency changes introduced in the `next/core-decouple-fe` PR within the `core/` SDK. The key change is decoupling `Bot.Compat` from `Bot.Apps` so that both depend directly on `Bot.Core` as independent siblings. |
| 4 | |
| 5 | --- |
| 6 | |
| 7 | ## Before: Linear Dependency Chain |
| 8 | |
| 9 | Prior to this PR, `Bot.Compat` depended on `Bot.Apps`, which in turn depended on `Bot.Core`. This created a **linear chain** where Compat transitively pulled in everything from Apps. |
| 10 | |
| 11 | ```mermaid |
| 12 | graph BT |
| 13 | Core["Microsoft.Teams.Core<br/><i>net8.0 · net10.0</i><br/>Foundation Layer"] |
| 14 | Apps["Microsoft.Teams.Apps<br/><i>net8.0 · net10.0</i><br/>Teams Features Layer"] |
| 15 | Compat["Microsoft.Teams.Apps.BotBuilder<br/><i>net8.0 · net10.0</i><br/>Compatibility Layer"] |
| 16 | |
| 17 | Apps -->|"ProjectReference"| Core |
| 18 | Compat -->|"ProjectReference"| Apps |
| 19 | |
| 20 | style Core fill:#e1f5ff,stroke:#0d6efd |
| 21 | style Apps fill:#fff4e1,stroke:#fd7e14 |
| 22 | style Compat fill:#ffe1f5,stroke:#d63384 |
| 23 | ``` |
| 24 | |
| 25 | ### Problems with this structure |
| 26 | |
| 27 | - **Unnecessary coupling**: `Bot.Compat` only needs `Bot.Core` (activity model, conversation client, hosting), but was forced to take a dependency on the entire `Bot.Apps` layer (Teams-specific handlers, routing, streaming, Teams API client). |
| 28 | - **Larger transitive closure**: Any consumer of `Bot.Compat` also pulled in `Bot.Apps` as a transitive dependency, even if they never used Teams-specific features. |
| 29 | - **Breaking change risk**: Changes to `Bot.Apps` could break `Bot.Compat` consumers even when the Compat layer only used Core types. |
| 30 | - **InternalsVisibleTo gap**: `Bot.Core` only exposed internals to `Bot.Apps`, so `Bot.Compat` had to go through Apps to access Core internals. |
| 31 | |
| 32 | --- |
| 33 | |
| 34 | ## After: Sibling Architecture |
| 35 | |
| 36 | This PR changes `Bot.Compat` to reference `Bot.Core` directly instead of `Bot.Apps`. Both `Apps` and `Compat` are now **independent siblings** that share only the `Core` foundation. |
| 37 | |
| 38 | ```mermaid |
| 39 | graph BT |
| 40 | Core["Microsoft.Teams.Core<br/><i>net8.0 · net10.0</i><br/>Foundation Layer"] |
| 41 | Apps["Microsoft.Teams.Apps<br/><i>net8.0 · net10.0</i><br/>Teams Features Layer"] |
| 42 | Compat["Microsoft.Teams.Apps.BotBuilder<br/><i>net8.0 · net10.0</i><br/>Compatibility Layer"] |
| 43 | |
| 44 | Apps -->|"ProjectReference"| Core |
| 45 | Compat -->|"ProjectReference"| Core |
| 46 | |
| 47 | style Core fill:#e1f5ff,stroke:#0d6efd |
| 48 | style Apps fill:#fff4e1,stroke:#fd7e14 |
| 49 | style Compat fill:#ffe1f5,stroke:#d63384 |
| 50 | ``` |
| 51 | |
| 52 | --- |
| 53 | |
| 54 | ## Side-by-Side Comparison |
| 55 | |
| 56 | ```mermaid |
| 57 | graph TB |
| 58 | subgraph "Before" |
| 59 | direction BT |
| 60 | B_Core["Bot.Core"] |
| 61 | B_Apps["Bot.Apps"] |
| 62 | B_Compat["Bot.Compat"] |
| 63 | B_Apps -->|"ProjectReference"| B_Core |
| 64 | B_Compat -->|"ProjectReference"| B_Apps |
| 65 | end |
| 66 | |
| 67 | subgraph "After" |
| 68 | direction BT |
| 69 | A_Core["Bot.Core"] |
| 70 | A_Apps["Bot.Apps"] |
| 71 | A_Compat["Bot.Compat"] |
| 72 | A_Apps -->|"ProjectReference"| A_Core |
| 73 | A_Compat -->|"ProjectReference"| A_Core |
| 74 | end |
| 75 | |
| 76 | style B_Core fill:#e1f5ff |
| 77 | style B_Apps fill:#fff4e1 |
| 78 | style B_Compat fill:#ffe1f5 |
| 79 | style A_Core fill:#e1f5ff |
| 80 | style A_Apps fill:#fff4e1 |
| 81 | style A_Compat fill:#ffe1f5 |
| 82 | ``` |
| 83 | |
| 84 | | Metric | Before | After | |
| 85 | |--------|--------|-------| |
| 86 | | Dependency depth from Compat | 3 (Compat → Apps → Core) | 2 (Compat → Core) | |
| 87 | | Compat's transitive project refs | 2 (Apps + Core) | 1 (Core) | |
| 88 | | Packages coupled to Bot.Apps | Apps + Compat | Apps only | |
| 89 | | Core InternalsVisibleTo | Apps, Core.UnitTests | Apps, **Compat**, Core.UnitTests | |
| 90 | |
| 91 | --- |
| 92 | |
| 93 | ## What Changed |
| 94 | |
| 95 | ### 1. `Bot.Compat.csproj` — dependency target changed |
| 96 | |
| 97 | ```diff |
| 98 | <ItemGroup> |
| 99 | - <ProjectReference Include="..\Microsoft.Teams.Apps\Microsoft.Teams.Apps.csproj" /> |
| 100 | + <ProjectReference Include="..\Microsoft.Teams.Core\Microsoft.Teams.Core.csproj" /> |
| 101 | </ItemGroup> |
| 102 | ``` |
| 103 | |
| 104 | ### 2. `Bot.Core.csproj` — InternalsVisibleTo added for Compat |
| 105 | |
| 106 | ```diff |
| 107 | <ItemGroup> |
| 108 | <InternalsVisibleTo Include="Microsoft.Teams.Core.UnitTests" /> |
| 109 | <InternalsVisibleTo Include="Microsoft.Teams.Apps" /> |
| 110 | + <InternalsVisibleTo Include="Microsoft.Teams.Apps.BotBuilder" /> |
| 111 | </ItemGroup> |
| 112 | ``` |
| 113 | |
| 114 | ### 3. Compat source code — rewritten to use Core types directly |
| 115 | |
| 116 | Types in `Bot.Compat` (e.g., `ActivitySchemaMapper`, `TeamsApiClient`, `CompatHostingExtensions`) were updated to import from `Microsoft.Teams.Core` namespaces instead of going through `Microsoft.Teams.Apps`. |
| 117 | |
| 118 | --- |
| 119 | |
| 120 | ## InternalsVisibleTo Relationships |
| 121 | |
| 122 | Before, only `Bot.Apps` could access Core internals. Now both sibling packages can. |
| 123 | |
| 124 | ```mermaid |
| 125 | graph LR |
| 126 | Core["Bot.Core"] |
| 127 | Apps["Bot.Apps"] |
| 128 | Compat["Bot.Compat"] |
| 129 | CoreTests["Bot.Core.UnitTests"] |
| 130 | AppsTests["Bot.Apps.UnitTests"] |
| 131 | |
| 132 | Core -.->|"InternalsVisibleTo"| Apps |
| 133 | Core -.->|"InternalsVisibleTo<br/>(new)"| Compat |
| 134 | Core -.->|"InternalsVisibleTo"| CoreTests |
| 135 | |
| 136 | Apps -.->|"InternalsVisibleTo"| AppsTests |
| 137 | |
| 138 | style Core fill:#e1f5ff |
| 139 | style Apps fill:#fff4e1 |
| 140 | style Compat fill:#ffe1f5 |
| 141 | style CoreTests fill:#f0f0f0 |
| 142 | style AppsTests fill:#f0f0f0 |
| 143 | ``` |
| 144 | |
| 145 | --- |
| 146 | |
| 147 | ## NuGet Dependencies Per Layer |
| 148 | |
| 149 | The external NuGet dependency layout is unchanged — but the transitive impact is different: |
| 150 | |
| 151 | ```mermaid |
| 152 | graph TD |
| 153 | subgraph "Bot.Core" |
| 154 | C1["AspNetCore.Authentication.JwtBearer"] |
| 155 | C2["AspNetCore.Authentication.OpenIdConnect"] |
| 156 | C3["System.Security.Cryptography.Pkcs"] |
| 157 | C4["Microsoft.Identity.Web.UI"] |
| 158 | C5["Microsoft.Identity.Web.AgentIdentities"] |
| 159 | end |
| 160 | |
| 161 | subgraph "Bot.Apps" |
| 162 | A1["(no external NuGet packages)"] |
| 163 | end |
| 164 | |
| 165 | subgraph "Bot.Compat" |
| 166 | X1["Microsoft.Bot.Builder.Integration<br/>.AspNet.Core 4.22.3"] |
| 167 | end |
| 168 | |
| 169 | style C1 fill:#e1f5ff |
| 170 | style C2 fill:#e1f5ff |
| 171 | style C3 fill:#e1f5ff |
| 172 | style C4 fill:#e1f5ff |
| 173 | style C5 fill:#e1f5ff |
| 174 | style A1 fill:#fff4e1 |
| 175 | style X1 fill:#ffe1f5 |
| 176 | ``` |
| 177 | |
| 178 | **Before**: A `Bot.Compat` consumer transitively received all NuGet packages from Core **plus** the entire `Bot.Apps` assembly. |
| 179 | |
| 180 | **After**: A `Bot.Compat` consumer only receives Core's NuGet packages. `Bot.Apps` is no longer in the transitive closure. |
| 181 | |
| 182 | --- |
| 183 | |
| 184 | ## Sample Application Dependency Patterns |
| 185 | |
| 186 | Samples demonstrate three independent entry points: |
| 187 | |
| 188 | ```mermaid |
| 189 | graph BT |
| 190 | Core["Bot.Core"] |
| 191 | Apps["Bot.Apps"] |
| 192 | Compat["Bot.Compat"] |
| 193 | |
| 194 | CoreBot["CoreBot<br/><i>net10.0</i>"] |
| 195 | TeamsBot["TeamsBot<br/><i>net10.0</i>"] |
| 196 | CompatBot["CompatBot<br/><i>net8.0</i>"] |
| 197 | |
| 198 | CoreBot --> Core |
| 199 | TeamsBot --> Apps |
| 200 | CompatBot --> Compat |
| 201 | |
| 202 | Apps --> Core |
| 203 | Compat --> Core |
| 204 | |
| 205 | style Core fill:#e1f5ff,stroke:#0d6efd |
| 206 | style Apps fill:#fff4e1,stroke:#fd7e14 |
| 207 | style Compat fill:#ffe1f5,stroke:#d63384 |
| 208 | style CoreBot fill:#f0f0f0 |
| 209 | style TeamsBot fill:#f0f0f0 |
| 210 | style CompatBot fill:#f0f0f0 |
| 211 | ``` |
| 212 | |
| 213 | | Entry Point | When to Use | |
| 214 | |-------------|-------------| |
| 215 | | **Bot.Core** directly | Minimal bots needing only core activity handling, middleware, and conversation client | |
| 216 | | **Bot.Apps** | Teams-specific bots with typed handlers, routing, streaming, Teams API client | |
| 217 | | **Bot.Compat** | Migrating existing Bot Framework v4 bots — no longer pulls in Bot.Apps transitively | |
| 218 | |
| 219 | --- |
| 220 | |
| 221 | ## Design Rationale |
| 222 | |
| 223 | 1. **Decoupled Compat from Apps**: `Bot.Compat` only needs Core primitives (activities, conversation client, hosting). Removing the Apps dependency eliminates unnecessary coupling. |
| 224 | 2. **Smaller transitive closure**: Consumers of `Bot.Compat` no longer pull in the entire Teams-specific layer (`Bot.Apps`) as a transitive dependency. |
| 225 | 3. **Independent evolution**: `Bot.Apps` and `Bot.Compat` can now be versioned and modified independently without risk of cross-impact. |
| 226 | 4. **Direct internal access**: Adding `InternalsVisibleTo` for Compat on Core removes the need to route through Apps to access shared infrastructure like `BotHttpClient` and serialization contexts. |
| 227 | 5. **Clearer architecture**: The sibling pattern makes the SDK's layering explicit — Core is the shared foundation, Apps adds Teams features, Compat bridges to Bot Framework v4. |
| 228 | |