Day 19. I stopped putting business logic in controllers. Not because it doesn’t work. Because it turns into a mess. I had code like this: @PostMapping("/users") public ResponseEntity<User> createUser(@RequestBody User user) { if (userRepository.existsByEmail(user.getEmail())) { throw new RuntimeException("Email already exists"); } user.setCreatedAt(LocalDateTime.now()); return ResponseEntity.ok(userRepository.save(user)); } Looks fine. Until your project grows. Now your controller is: → validating business rules → talking to the database → handling the request That’s not a controller. That’s 3 responsibilities in one place. And it only gets worse with time. That’s when it clicked. Controllers shouldn’t think. They should delegate. So I changed it. (see implementation below 👇) What I gained: → Clarity — each layer has one job → Testability — logic tested without HTTP → Reusability — service can be used anywhere The hard truth: → Fat controllers work in tutorials → They fail in real systems → Senior devs spot this instantly Writing endpoints is easy. Keeping your layers clean is what makes you a backend developer. Are your controllers doing too much? 👇 Drop it below #SpringBoot #Java #BackendDevelopment #CleanCode #JavaDeveloper
Why Controllers Shouldn't Think in Spring Boot
More Relevant Posts
-
🚫 Why We Should Avoid Excessive Lookups in UAT & Production It’s easy to rely on lookup queries during development — they work fine in small datasets and local environments. But what works in dev can break badly in UAT and Production. 🔍 What changes in UAT & Prod? Real users 👥 Large data volumes 📊 High traffic 🚀 Now, those “simple lookups” start becoming expensive operations. ⚠️ Why excessive lookups are dangerous: ❌ Increased latency Every lookup hits the database → slower response time ❌ Database overload Thousands of repeated queries can exhaust DB connections ❌ Scalability issues System struggles as users increase ❌ N+1 query problem Loop-based queries multiply DB calls drastically ❌ Unnecessary cost More DB usage = higher infrastructure cost 💡 Why We should NEVER rely on repeated lookups: 👉 Databases are not meant for repeated fetching of the same data 👉 Network calls are expensive in distributed systems 👉 Performance issues often appear only in UAT/Prod, not in dev ✅ What to do instead: ✔️ Use caching (store frequently used data in memory) ✔️ Fetch data in bulk (avoid queries inside loops) ✔️ Use joins instead of multiple queries ✔️ Add proper indexing ✔️ Design APIs to minimize DB calls 🚀 Real-world systems focus on reducing database hits to ensure high performance and reliability. 👉 Key takeaway: “Code that works in development is not enough — it must scale in production.” #SystemDesign #BackendDevelopment #Performance #Java #Scalability
To view or add a comment, sign in
-
“Can I use GET to create data? Or POST to fetch it?” 🤔 Looks harmless… until it hits production. We all know: GET → read data POST → change data But here’s where it gets serious 👇 ⚠️ If you use GET to create data Technically it works. But under the hood: -> GET can be cached (browser / proxy / CDN) -> It can be retried automatically -> It can be triggered again via URL or refresh 💥 Real-world impact (Banking example) Imagine a money transfer API built using GET: User clicks “Pay” → ₹10,000 transferred Network retries → ₹10,000 transferred again Page refresh → ₹10,000 transferred again Now suddenly: 👉 ₹30,000 is gone 👉 Logs look normal 👉 No obvious error This becomes a production incident, not a bug. ❓ What about POST for fetching? Used sometimes (complex or secure requests), but for simple reads → GET is predictable and safe. 🎯 Simple rule (that saves systems) If nothing should change → GET If something changes → POST Small API decisions like this decide whether your system is 👉 predictable 👉 or a nightmare in production #Java #SpringBoot #Backend #REST #SystemDesign #ApiDesign #Interview
To view or add a comment, sign in
-
Day 27. I put @Transactional in my controller. My senior caught it in code review. That conversation changed how I think about layers. I had this: @RestController public class UserController { @Transactional @PostMapping("/users") public ResponseEntity<?> createUser(@RequestBody User user) { userRepository.save(user); return ResponseEntity.ok("User created"); } } It worked. No errors. Everything looked fine. Looked clean. Until a senior looked at it. Here’s what actually happens: → Transaction starts at the controller → It stays open longer than needed → Higher chance of locks & performance issues → Blurred business logic boundaries That’s when it clicked. Transactions don’t belong in controllers. Controllers should: → Handle HTTP → Delegate work Not manage transactions. So I changed it. (see implementation below 👇) What I learned: → Transactions define business boundaries → They should live close to business logic (service layer) → Not at the API layer The hard truth: → Working code and well-designed code are two different things → Most tutorials only teach you the first one Writing working code is easy. Placing responsibility in the right layer is what makes you a backend developer. If your senior reviewed your controller today — would @Transactional be there? 👇 Drop your take #SpringBoot #Java #BackendDevelopment #CleanCode #Hibernate #JavaDeveloper
To view or add a comment, sign in
-
-
🚀 Most developers use @Transactional… but don’t understand how it actually works. And that’s where bugs start. Let’s simplify it 👇 --- 👉 What does @Transactional do? It ensures that a group of database operations: ✔ Either ALL succeed ✔ Or ALL fail (rollback) --- 💡 Example: @Transactional public void placeOrder() { saveOrder(); makePayment(); } 👉 If makePayment() fails ❌ → saveOrder() will also be rolled back --- 🔥 But here’s the catch (VERY IMPORTANT): @Transactional works using Spring AOP (proxy) 👉 Spring creates a proxy around your class → That proxy manages transaction start/commit/rollback --- ⚠️ Common mistake (many developers miss this): @Service public class OrderService { @Transactional public void createOrder() { saveOrder(); // ❌ Transaction may NOT work } @Transactional public void saveOrder() { // DB logic } } 👉 Why? Because internal method calls bypass the proxy 😮 --- ❌ Result: Transaction might not be applied properly --- ✅ Fix: Option 1: Call method from another service (bean) Option 2: Design properly (separate responsibilities) --- 🔥 Real-world impact: In payment systems: ❌ Partial data saved ❌ Inconsistent state This can cause serious bugs 🚨 --- 📌 Key Takeaway: @Transactional is NOT magic → It depends on proxy behavior Understand this = avoid production bugs --- Follow for more real backend learnings 🚀 #SpringBoot #Java #BackendDevelopment #SystemDesign #SoftwareEngineer
To view or add a comment, sign in
-
-
Most developers fix bugs. Few actually understand why the bug exists. Recently, I debugged a simple issue: Two currencies (COP, PEN) were missing from a dropdown. At first, it looked like a UI problem. But digging deeper revealed something more interesting. What was happening? * Dropdown values were coming from a transaction table * Selected value was inserted back into the same system * Which again fed the dropdown DB ->Dropdown ->Insert -> DB The problem: This created a circular dependency.🥲 * If a currency doesn’t exist in the DB * It won’t appear in the dropdown * If it’s not in the dropdown * It can’t be inserted * If it’s not inserted * It will never exist in the DB New data becomes impossible to introduce. Hidden issue: tight coupling The UI was tightly coupled with the transaction database. Meaning: UI behavior depended directly on the current state of the DB. Why this is a problem: * Any DB limitation immediately affects the UI * Introducing new values becomes difficult * Changes in one layer impact multiple parts of the system * The system becomes fragile and harder to scale Example: Allowed roles dropdown = SELECT DISTINCT role FROM user_table Now try adding a new role: ADMIN * Not in DB * Not in dropdown * Cannot be assigned * Never gets into DB Same circular problem. Key learning: UI should be driven by master or configuration tables, not transactional data. Ideal approach: Master Table → UI → Insert → Transaction Table 📝Takeaways * Separate master data from transaction data * Avoid tight coupling between UI and database state * Watch for circular dependencies in legacy systems Debugging is not just about fixing errors. It is about understanding systems. #SoftwareEngineering #Debugging #SystemDesign #Backend #Java #designpatterns #microservices #springboot
To view or add a comment, sign in
-
-
Most developers don’t structure their backend properly. And it shows when the project starts growing. Here’s how I structure my backend projects 👇 (An ASP.NET Core project I’m currently working on) 📁 Clean separation of concerns: Controllers → handle requests Services → business logic Repositories → data access DTOs → data transfer Models → database structure 📁 Supporting layers: Middleware → request pipeline control Helpers → reusable utilities Mappings → AutoMapper configs Enums → clean constant handling 📁 Core configs: JWT Authentication setup Dependency Injection properly wired AutoMapper integrated Password hashing included This structure helps me: - Scale easily - Debug faster - Work with teams smoothly - Keep code maintainable Most beginners mix everything in one place, which works… until it doesn’t. If you’re building real-world systems, structure is not optional. Are you using a proper backend structure or still figuring it out? #SoftwareEngineering #BackendDevelopment #AspNetCore #DotNet #SystemDesign #APIDevelopment #BuildInPublic
To view or add a comment, sign in
-
-
Your transaction works perfectly… ✅ Until you call 𝗮𝗻𝗼𝘁𝗵𝗲𝗿 𝗺𝗲𝘁𝗵𝗼𝗱 inside it. Suddenly things behave… differently. Why? 𝗣𝗿𝗼𝗽𝗮𝗴𝗮𝘁𝗶𝗼𝗻. If you’re using `@Transactional` and not thinking about propagation, you’re basically saying: “Spring… you decide.” So what is propagation? It defines how a transaction behaves when one method calls another. Simple idea. But huge impact. Let’s break it down with real-world scenarios 🔹 REQUIRED (default) “Join if exists, else create” 💡 Example: Placing an order → Payment service called Both run in the "same transaction". If payment fails → everything rolls back. 🔹 REQUIRES_NEW “Always start fresh” 💡 Example: Order fails ❌ But you still want to "log the failure" Logging runs in a separate transaction → it gets saved ✅ 🔹 SUPPORTS “Join if exists, else run without transaction” 💡 Example: Fetching optional audit data Transaction or not… it doesn’t care. 🔹 NOT_SUPPORTED “Run without transaction” 💡 Example: Heavy read operation where transaction overhead is unnecessary Suspends existing transaction ⏸️ 🔹 MANDATORY “Transaction must exist” 💡 Example: Critical financial operation If no transaction → throw error 🚨 🔹 NEVER “Transaction must NOT exist” 💡 Example: A method that should never be part of a transaction If one exists → exception 💥 🔹 NESTED “Transaction inside a transaction” 💡 Example: Partial rollback scenario Inner transaction fails → outer can still continue Here’s the real insight Most bugs in transaction handling don’t come from syntax… They come from 𝘄𝗿𝗼𝗻𝗴 𝗽𝗿𝗼𝗽𝗮𝗴𝗮𝘁𝗶𝗼𝗻 choices. Using `@Transactional` is easy. Using it correctly is what makes you a solid backend developer. Next time you add `@Transactional`, don’t stop there… Ask: “How should this transaction behave?” #CoreJava #SpringBoot #JavaDeveloper #Transactional #BackendDevelopment #SoftwareEngineering #Developers #Programming #Developers #RDBMS #SQL #JPA #Hibernate #Database #Microservices #aswintech #SystemDesign
To view or add a comment, sign in
-
🚀 DAY 41/100 – Controller Design Best Practices & DTO vs Entity In backend development, writing APIs is not enough. How you structure your controllers and manage data flow defines the quality, scalability, and maintainability of your application. Two concepts that every Spring Boot developer must master early: 👉 Controller Design Best Practices 👉 DTO vs Entity separation A well-designed controller should act only as a request handler, not a business logic layer. Key principles: • Keep controllers thin (delegate logic to service layer) • Use proper HTTP methods & status codes • Validate requests using @Valid • Maintain consistent API structure (/api/v1/...) • Handle exceptions using global handlers (@RestControllerAdvice) 📘 DTO vs Entity (Critical Concept) One of the most common mistakes in early backend projects is exposing entities directly. Why this is a problem: • Security risks (sensitive fields exposed) • Tight coupling between API & database • Poor flexibility for future changes Solution: ✔ Use DTOs for request/response ✔ Keep entities internal to persistence layer ✔ Map between DTO ↔ Entity in service layer 📘 What’s Covered in the Document • Controller responsibilities & anti-patterns • DTO vs Entity deep comparison • Mapping strategies (manual + tools) • Layered architecture breakdown • Interview questions with detailed answers 📄 I’ve compiled everything into a clean, revision-friendly document with examples and code snippets. If you're preparing for Java / Spring Boot / Backend roles (0–3 YOE): 📌 Save this — these are core concepts asked in interviews 🔁 Repost — helps others learning backend fundamentals Follow Surya Mahesh Kolisetty and continue the journey with #100DaysOfBackend #Java #SpringBoot #BackendDevelopment #API #CFBR #Connections #SoftwareEngineering #InterviewPrep #Developers #Programming #LearningInPublic #CleanCode #SystemDesign
To view or add a comment, sign in
-
Stop using AsNoTracking() blindly. ✋ You might be creating a hidden bug without knowing it. Here's what most .NET devs don't know 👇 You use 𝗔𝘀𝗡𝗼𝗧𝗿𝗮𝗰𝗸𝗶𝗻𝗴() for read-only queries. Smart move — it skips the Change Tracker and boosts performance. But what happens when your query has Include() and the same related entity appears in multiple rows? EF creates 𝗱𝘂𝗽𝗹𝗶𝗰𝗮𝘁𝗲 𝗼𝗯𝗷𝗲𝗰𝘁𝘀 for every occurrence. 😬 Same entity, 10 rows? You get 10 separate objects in memory. → Higher memory usage → Same entity treated as different objects (Distinct, GroupBy, Dictionary lookups all break) → Unexpected bugs in your business logic ✅ The fix? 𝗔𝘀𝗡𝗼𝗧𝗿𝗮𝗰𝗸𝗶𝗻𝗴𝗪𝗶𝘁𝗵𝗜𝗱𝗲𝗻𝘁𝗶𝘁𝘆𝗥𝗲𝘀𝗼𝗹𝘂𝘁𝗶𝗼𝗻() Available since EF Core 5.0 — it gives you the best of both worlds: → Entities are NOT tracked (no SaveChanges overhead) → Reuses the SAME object instance for duplicate entities → Maintains object identity across the result set → Uses less memory on large result sets 🔎 𝗪𝗵𝗲𝗻 𝘁𝗼 𝘂𝘀𝗲 𝘄𝗵𝗮𝘁: 𝗔𝘀𝗡𝗼𝗧𝗿𝗮𝗰𝗸𝗶𝗻𝗴() → Simple flat queries with no Include() or joins → Maximum raw performance → Perfectly fine when duplicate entities aren't a concern 𝗔𝘀𝗡𝗼𝗧𝗿𝗮𝗰𝗸𝗶𝗻𝗴𝗪𝗶𝘁𝗵𝗜𝗱𝗲𝗻𝘁𝗶𝘁𝘆𝗥𝗲𝘀𝗼𝗹𝘂𝘁𝗶𝗼𝗻() → Queries with Include() or navigation properties → When the same entity can appear multiple times → When your code compares, groups, or deduplicates entities 𝗡𝗼 𝗺𝗼𝗱𝗶𝗳𝗶𝗲𝗿 (𝗱𝗲𝗳𝗮𝘂𝗹𝘁 𝘁𝗿𝗮𝗰𝗸𝗶𝗻𝗴) → When you need to update/delete entities via SaveChanges() ⚡ 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 𝗼𝗿𝗱𝗲𝗿 (𝗳𝗮𝘀𝘁𝗲𝘀𝘁 → 𝘀𝗹𝗼𝘄𝗲𝘀𝘁): 1️⃣ AsNoTracking() 2️⃣ AsNoTrackingWithIdentityResolution() 3️⃣ Default (full tracking) Speed difference between 1 and 2? Barely noticeable. But 1 creates duplicate objects for the same entity — and that can break your logic. Don't just slap AsNoTracking() everywhere and call it a day. Pick the right tool for the job. 💡
To view or add a comment, sign in
-
-
🚀 Day 28 – Bean Scopes: Managing Object Lifecycles the Right Way In Spring-based systems, Bean Scope defines how long an object lives and how many instances are created. It directly impacts memory usage, performance, and thread safety — making it an important architectural decision. 🔹 1. Singleton (Default Scope) ✔ One instance per Spring container ✔ Shared across the application ➡ Best for: Stateless services Utility components ⚠️ Be careful with mutable state (thread-safety concerns) 🔹 2. Prototype Scope ✔ New instance every time requested ➡ Best for: Stateful objects Short-lived processing logic ⚠️ Spring does NOT manage full lifecycle (e.g., destruction) 🔹 3. Request Scope (Web Apps) ✔ One bean per HTTP request ➡ Best for: Request-specific data User context 🔹 4. Session Scope ✔ One bean per user session ➡ Best for: User preferences Session-level caching ⚠️ Can increase memory usage if misused 🔹 5. Application Scope ✔ One bean per ServletContext ➡ Shared across the entire application lifecycle ➡ Rarely used but useful for global configs 🔹 6. Choosing the Right Scope Matters Wrong scope can lead to: ❌ Memory leaks ❌ Concurrency issues ❌ Unexpected behavior ➡ Always align scope with object responsibility & lifecycle 🔹 7. Stateless Design is King Prefer singleton + stateless beans ➡ Easier scaling ➡ Better performance ➡ Fewer concurrency bugs 🔹 8. Scope + Dependency Injection Gotcha Injecting prototype into singleton? ➡ You’ll still get a single instance unless handled properly ➡ Use ObjectFactory / Provider for dynamic resolution 🔥 Architect’s Takeaway Bean scope is not just configuration — it’s an architectural decision. Choosing the right scope ensures: ✔ Efficient memory usage ✔ Better scalability ✔ Thread-safe designs ✔ Predictable behavior 💬 Which bean scope do you use the most — and have you ever faced issues due to wrong scope? #100DaysOfJavaArchitecture #SpringBoot #BeanScopes #Java #Microservices #SystemDesign #TechLeadership
To view or add a comment, sign in
-
Explore related topics
- Steps to Become a Back End Developer
- How Developers Translate Business Rules Into Code
- Backend Developer Interview Questions for IT Companies
- SOLID Principles for Junior Developers
- Ways to Improve Coding Logic for Free
- Best Practices for Writing Clean Code
- Coding Best Practices to Reduce Developer Mistakes
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development
Hot take: If your controller has if/else logic or directly calls the repository — It’s already doing too much. Controllers should delegate. Services should decide. Is your controller clean or overloaded? 👇