Submodule 3
Submodule 3 of Backend Development Mini-Quest
Backend Frameworks
Flask vs Spring Boot
| Aspect | Flask (Python) | Spring Boot (Java) |
|---|---|---|
| Philosophy | Microframework - minimal, add as needed | Full-featured - everything included |
| Learning Curve | Low - simple to start | Moderate - more concepts upfront |
| Boilerplate | Minimal code needed | More verbose, explicit |
| Type Safety | Dynamic typing | Static typing (compile-time checks) |
| Performance | Good (fast enough for most) | Excellent (JVM optimization) |
| Best For | Prototypes, ML APIs, small teams | Enterprise apps, microservices, large teams |
| Deployment | Lightweight (MB) | Heavier (tens of MB) |
When to Use Each Framework
Choose Flask When:
- Rapid Prototyping: Need an MVP in hours/days
- Data Science/ML: Integrating with NumPy, Pandas, TensorFlow, PyTorch
- Simple REST APIs: Straightforward CRUD without complex business logic
- Small Teams: Quick onboarding, less ceremony
- Python Ecosystem: Leveraging existing Python libraries
Choose Spring Boot When:
- Enterprise Applications: Banking, insurance, healthcare systems
- Microservices: Spring Cloud ecosystem for distributed systems
- High Performance: Stock trading, payment processing, real-time systems
- Large Teams: Strong typing helps prevent bugs across teams
- Long-Term Projects: Better for long term yer maintenance
Flask Architecture & Structure
Flask: Minimal Setup
Installation:
pip install flask flask-sqlalchemy flask-cors
Project Structure:
social_api/
├── app.py # Main application
├── models.py # Database models
├── routes/
│ ├── users.py # User endpoints
│ └── posts.py # Post endpoints
├── requirements.txt # Dependencies
└── config.py # Configuration
Flask Core Concepts
1. Routing (Simple Functions)
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/api/posts', methods=['GET'])
def get_posts():
posts = Post.query.all()
return jsonify([p.to_dict() for p in posts])
@app.route('/api/posts', methods=['POST'])
def create_post():
data = request.json
post = Post(title=data['title'], content=data['content'])
db.session.add(post)
db.session.commit()
return jsonify(post.to_dict()), 201
Key Points:
- Routes are simple functions decorated with
@app.route - Manually handle JSON parsing with
request.json - Explicitly return status codes
2. Database with SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///social.db'
db = SQLAlchemy(app)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def to_dict(self):
return {
'id': self.id,
'title': self.title,
'content': self.content
}
3. Request Validation (Manual)
@app.route('/api/users', methods=['POST'])
def register():
data = request.json
# Manual validation
if not data.get('username'):
return jsonify({'error': 'Username required'}), 400
if not data.get('email'):
return jsonify({'error': 'Email required'}), 400
if len(data.get('password', '')) < 8:
return jsonify({'error': 'Password too short'}), 400
# Create user
user = User(username=data['username'], email=data['email'])
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201
Flask Strengths include fast development, being direct, being flexible for different structures, and being easy to integrate with data. Flask Challenges can be derived from strengths, like having messy code due to its flexibility and having errors from typos.
Spring Boot Architecture
Project Structure:
my-spring-app/
├── src/main/java/com/example/demo/
│ ├── DemoApplication.java # Main entry point
│ ├── controller/ # REST endpoints
│ │ └── PostController.java
│ ├── service/ # Business logic
│ │ └── PostService.java
│ ├── repository/ # Database access
│ │ └── PostRepository.java
│ └── entity/ # Database models
│ └── Post.java
├── src/main/resources/
│ └── application.properties # Configuration
└── pom.xml # Dependencies (Maven)
Spring Boot Core Concepts
Inversion of Control (IoC)
Without IoC (Tight Coupling):
public class OrderService {
private EmailService emailService = new EmailService(); // ❌ Creates dependency
}
With IoC (Loose Coupling):
@Service
public class OrderService {
private final EmailService emailService;
@Autowired // Spring injects this automatically
public OrderService(EmailService emailService) {
this.emailService = emailService;
}
}
Benefits:
- Easy to test (inject mock EmailService)
- Swap implementations without changing code
- Spring manages object lifecycle
Layered Architecture
Controller Layer (HTTP Handling):
@RestController
@RequestMapping("/api/posts")
public class PostController {
private final PostService postService;
@Autowired
public PostController(PostService postService) {
this.postService = postService;
}
@GetMapping
public ResponseEntity<List<Post>> getAllPosts() {
return ResponseEntity.ok(postService.findAll());
}
@PostMapping
public ResponseEntity<Post> createPost(@RequestBody Post post) {
Post created = postService.create(post);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@GetMapping("/{id}")
public ResponseEntity<Post> getPost(@PathVariable Long id) {
return ResponseEntity.ok(postService.findById(id));
}
}
Service Layer (Business Logic):
@Service
@Transactional // Automatic transaction management
public class PostService {
private final PostRepository postRepository;
@Autowired
public PostService(PostRepository postRepository) {
this.postRepository = postRepository;
}
public List<Post> findAll() {
return postRepository.findAll();
}
public Post findById(Long id) {
return postRepository.findById(id)
.orElseThrow(() -> new PostNotFoundException(id));
}
public Post create(Post post) {
// Validation
if (post.getTitle() == null || post.getTitle().isEmpty()) {
throw new IllegalArgumentException("Title is required");
}
return postRepository.save(post);
}
}
Repository Layer (Database Access):
@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
// Spring generates implementation automatically!
List<Post> findByTitleContaining(String keyword);
List<Post> findByUserId(Long userId);
@Query("SELECT p FROM Post p WHERE p.createdAt > ?1")
List<Post> findRecentPosts(LocalDateTime since);
}
Entity Layer (Database Model):
@Entity
@Table(name = "posts")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 200)
private String title;
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
@ManyToOne
@JoinColumn(name = "user_id")
private User author;
@Column(name = "created_at")
private LocalDateTime createdAt;
// Constructors, getters, setters
}
3. Automatic Validation
@Entity
public class User {
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50)
private String username;
@Email(message = "Email must be valid")
@NotBlank
private String email;
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;
}
// In Controller
@PostMapping
public ResponseEntity<User> create(@Valid @RequestBody User user) {
// @Valid automatically validates based on annotations
return ResponseEntity.ok(userService.create(user));
}
Spring Boot Strengths are mainly in the fact that Spring has a clear structure to follow, along with others like type safety and built in validation. Spring Boot Challenges include the steeper learning curve and needing more setup and code due to its structure and larger scale. —
How code looks in Spring Boot and Flask
Creating a Post Endpoint
Flask:
@app.route('/api/posts', methods=['POST'])
def create_post():
data = request.json
if not data.get('title'):
return {'error': 'Title required'}, 400
post = Post(title=data['title'], content=data['content'])
db.session.add(post)
db.session.commit()
return post.to_dict(), 201
Spring Boot:
// Controller
@PostMapping
public ResponseEntity<Post> createPost(@Valid @RequestBody Post post) {
Post created = postService.create(post);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
// Service
@Transactional
public Post create(Post post) {
validatePost(post);
return postRepository.save(post);
}
// Repository
public interface PostRepository extends JpaRepository<Post, Long> {
// Spring provides save() automatically
}
Key Differences:
- Flask: One function does everything
- Spring: Separated into Controller → Service → Repository
- Flask: Manual validation
- Spring:
@Validannotation + automatic validation - Flask: Manual transaction management
- Spring:
@Transactionalhandles it
Configuration Files
Flask Configuration
# config.py
class Config:
SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = 'your-secret-key'
# app.py
app = Flask(__name__)
app.config.from_object(Config)
Spring Boot Configuration
# application.properties
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
server.port=8080
Testing
Flask Testing
def test_create_post():
response = client.post('/api/posts',
json={'title': 'Test', 'content': 'Content'})
assert response.status_code == 201
assert response.json['title'] == 'Test'
Spring Boot Testing
@SpringBootTest
@AutoConfigureMockMvc
public class PostControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testCreatePost() throws Exception {
mockMvc.perform(post("/api/posts")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"title\":\"Test\",\"content\":\"Content\"}"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.title").value("Test"));
}
}
Key Takeaways
Flask
- Simplicity: Write code, not configuration
- Flexibility: Choose your own tools
- Speed: Prototype to production quickly
- Python Power: Leverage data science ecosystem
Spring Boot
- Convention over Configuration: Sensible defaults
- Enterprise Ready: Production features built-in
- Type Safety: Catch bugs early
- Scalability: Built for millions of requests
Summary
- Flask: Best for small-to-medium projects, ML APIs, rapid development
- Spring Boot: Best for enterprise apps, microservices, large teams
- Both are systems that work, it is important to choose based on the needs of the system.
Quick Quiz — Module 3 (Interactive)
Answer the five questions below. Pick the best choice (A–D) and click Submit to see your score.
Vocab Picker — Module 3
Three short vocabulary questions. For each, choose the best term from the dropdown and click Submit to see your total score.
Up Next
In the next submodule, you’ll wrap up your learning on the backend. Keep progressing in order to receive a certificate for completing this module.