@corbat-tech/coding-standards-mcp 1.0.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +233 -337
- package/dist/agent.d.ts +5 -6
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +95 -217
- package/dist/agent.js.map +1 -1
- package/dist/analysis/code-analyzer.d.ts +44 -0
- package/dist/analysis/code-analyzer.d.ts.map +1 -0
- package/dist/analysis/code-analyzer.js +528 -0
- package/dist/analysis/code-analyzer.js.map +1 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +112 -0
- package/dist/errors.js.map +1 -0
- package/dist/guardrails.d.ts +35 -0
- package/dist/guardrails.d.ts.map +1 -0
- package/dist/guardrails.js +303 -0
- package/dist/guardrails.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +36 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +63 -0
- package/dist/logger.js.map +1 -0
- package/dist/metrics.d.ts +40 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +97 -0
- package/dist/metrics.js.map +1 -0
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.d.ts.map +1 -1
- package/dist/profiles.js +239 -108
- package/dist/profiles.js.map +1 -1
- package/dist/prompts.js +1 -1
- package/dist/prompts.js.map +1 -1
- package/dist/tools/definitions.d.ts +143 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +229 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/handlers/get-context.d.ts +12 -0
- package/dist/tools/handlers/get-context.d.ts.map +1 -0
- package/dist/tools/handlers/get-context.js +233 -0
- package/dist/tools/handlers/get-context.js.map +1 -0
- package/dist/tools/handlers/health.d.ts +11 -0
- package/dist/tools/handlers/health.d.ts.map +1 -0
- package/dist/tools/handlers/health.js +57 -0
- package/dist/tools/handlers/health.js.map +1 -0
- package/dist/tools/handlers/index.d.ts +12 -0
- package/dist/tools/handlers/index.d.ts.map +1 -0
- package/dist/tools/handlers/index.js +12 -0
- package/dist/tools/handlers/index.js.map +1 -0
- package/dist/tools/handlers/init.d.ts +12 -0
- package/dist/tools/handlers/init.d.ts.map +1 -0
- package/dist/tools/handlers/init.js +102 -0
- package/dist/tools/handlers/init.js.map +1 -0
- package/dist/tools/handlers/profiles.d.ts +11 -0
- package/dist/tools/handlers/profiles.d.ts.map +1 -0
- package/dist/tools/handlers/profiles.js +25 -0
- package/dist/tools/handlers/profiles.js.map +1 -0
- package/dist/tools/handlers/search.d.ts +12 -0
- package/dist/tools/handlers/search.d.ts.map +1 -0
- package/dist/tools/handlers/search.js +58 -0
- package/dist/tools/handlers/search.js.map +1 -0
- package/dist/tools/handlers/validate.d.ts +15 -0
- package/dist/tools/handlers/validate.d.ts.map +1 -0
- package/dist/tools/handlers/validate.js +71 -0
- package/dist/tools/handlers/validate.js.map +1 -0
- package/dist/tools/handlers/verify.d.ts +38 -0
- package/dist/tools/handlers/verify.d.ts.map +1 -0
- package/dist/tools/handlers/verify.js +172 -0
- package/dist/tools/handlers/verify.js.map +1 -0
- package/dist/tools/index.d.ts +22 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +75 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/schemas.d.ts +29 -0
- package/dist/tools/schemas.d.ts.map +1 -0
- package/dist/tools/schemas.js +20 -0
- package/dist/tools/schemas.js.map +1 -0
- package/dist/tools.js +2 -2
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +141 -71
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +92 -40
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/profiles/examples/microservice-kafka.yaml +122 -0
- package/profiles/examples/startup-fast.yaml +67 -0
- package/profiles/examples/strict-enterprise.yaml +62 -0
- package/profiles/templates/angular.yaml +614 -0
- package/profiles/templates/csharp-dotnet.yaml +529 -0
- package/profiles/templates/flutter.yaml +547 -0
- package/profiles/templates/go.yaml +1276 -0
- package/profiles/templates/java-spring-backend.yaml +326 -0
- package/profiles/templates/kotlin-spring.yaml +417 -0
- package/profiles/templates/nextjs.yaml +536 -0
- package/profiles/templates/nodejs.yaml +594 -0
- package/profiles/templates/python.yaml +546 -0
- package/profiles/templates/react.yaml +456 -0
- package/profiles/templates/rust.yaml +508 -0
- package/profiles/templates/vue.yaml +483 -0
|
@@ -510,3 +510,329 @@ technologies:
|
|
|
510
510
|
useArchUnit: true
|
|
511
511
|
testNamingPattern: "should_ExpectedBehavior_When_Condition"
|
|
512
512
|
integrationTestSuffix: "IT"
|
|
513
|
+
|
|
514
|
+
# ----------------------------------------------------------------------------
|
|
515
|
+
# CODE EXAMPLES - For AI Learning
|
|
516
|
+
# ----------------------------------------------------------------------------
|
|
517
|
+
codeExamples:
|
|
518
|
+
valueObject: |
|
|
519
|
+
// Value Object using Java Record (immutable, self-validating)
|
|
520
|
+
public record Money(BigDecimal amount, Currency currency) {
|
|
521
|
+
public Money {
|
|
522
|
+
Objects.requireNonNull(amount, "Amount cannot be null");
|
|
523
|
+
Objects.requireNonNull(currency, "Currency cannot be null");
|
|
524
|
+
if (amount.compareTo(BigDecimal.ZERO) < 0) {
|
|
525
|
+
throw new IllegalArgumentException("Amount cannot be negative");
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
public Money add(Money other) {
|
|
530
|
+
if (!this.currency.equals(other.currency)) {
|
|
531
|
+
throw new IllegalArgumentException("Cannot add different currencies");
|
|
532
|
+
}
|
|
533
|
+
return new Money(this.amount.add(other.amount), this.currency);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
entity: |
|
|
538
|
+
// Domain Entity with encapsulated business logic
|
|
539
|
+
public class Order {
|
|
540
|
+
private final OrderId id;
|
|
541
|
+
private final CustomerId customerId;
|
|
542
|
+
private OrderStatus status;
|
|
543
|
+
private final List<OrderLine> lines;
|
|
544
|
+
private Money total;
|
|
545
|
+
|
|
546
|
+
public Order(OrderId id, CustomerId customerId) {
|
|
547
|
+
this.id = Objects.requireNonNull(id);
|
|
548
|
+
this.customerId = Objects.requireNonNull(customerId);
|
|
549
|
+
this.status = OrderStatus.DRAFT;
|
|
550
|
+
this.lines = new ArrayList<>();
|
|
551
|
+
this.total = Money.ZERO;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
public void addLine(Product product, Quantity quantity) {
|
|
555
|
+
if (status != OrderStatus.DRAFT) {
|
|
556
|
+
throw new InvalidOperationException("Cannot modify non-draft order");
|
|
557
|
+
}
|
|
558
|
+
var line = new OrderLine(product, quantity);
|
|
559
|
+
lines.add(line);
|
|
560
|
+
recalculateTotal();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
public void confirm() {
|
|
564
|
+
if (lines.isEmpty()) {
|
|
565
|
+
throw new BusinessRuleViolationException("Cannot confirm empty order");
|
|
566
|
+
}
|
|
567
|
+
this.status = OrderStatus.CONFIRMED;
|
|
568
|
+
// Publish domain event
|
|
569
|
+
DomainEvents.publish(new OrderConfirmed(this.id, this.total));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
useCase: |
|
|
574
|
+
// Use Case / Application Service
|
|
575
|
+
@RequiredArgsConstructor
|
|
576
|
+
@Transactional
|
|
577
|
+
public class CreateOrderUseCase {
|
|
578
|
+
private final OrderRepository orderRepository;
|
|
579
|
+
private final CustomerRepository customerRepository;
|
|
580
|
+
private final DomainEventPublisher eventPublisher;
|
|
581
|
+
|
|
582
|
+
public OrderId execute(CreateOrderCommand command) {
|
|
583
|
+
// 1. Validate customer exists
|
|
584
|
+
var customer = customerRepository.findById(command.customerId())
|
|
585
|
+
.orElseThrow(() -> new EntityNotFoundException("Customer", command.customerId()));
|
|
586
|
+
|
|
587
|
+
// 2. Create order (domain logic)
|
|
588
|
+
var order = new Order(OrderId.generate(), customer.getId());
|
|
589
|
+
for (var item : command.items()) {
|
|
590
|
+
order.addLine(item.product(), item.quantity());
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// 3. Save
|
|
594
|
+
orderRepository.save(order);
|
|
595
|
+
|
|
596
|
+
// 4. Publish events
|
|
597
|
+
eventPublisher.publishAll(order.getDomainEvents());
|
|
598
|
+
|
|
599
|
+
return order.getId();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
controller: |
|
|
604
|
+
// REST Controller (Infrastructure Layer)
|
|
605
|
+
@RestController
|
|
606
|
+
@RequestMapping("/api/v1/orders")
|
|
607
|
+
@RequiredArgsConstructor
|
|
608
|
+
public class OrderController {
|
|
609
|
+
private final CreateOrderUseCase createOrderUseCase;
|
|
610
|
+
private final GetOrderQuery getOrderQuery;
|
|
611
|
+
|
|
612
|
+
@PostMapping
|
|
613
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
614
|
+
public ResponseEntity<OrderResponse> create(@Valid @RequestBody CreateOrderRequest request) {
|
|
615
|
+
var command = new CreateOrderCommand(
|
|
616
|
+
new CustomerId(request.customerId()),
|
|
617
|
+
request.items().stream()
|
|
618
|
+
.map(i -> new OrderItem(new ProductId(i.productId()), new Quantity(i.quantity())))
|
|
619
|
+
.toList()
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
var orderId = createOrderUseCase.execute(command);
|
|
623
|
+
var order = getOrderQuery.execute(orderId);
|
|
624
|
+
|
|
625
|
+
return ResponseEntity
|
|
626
|
+
.created(URI.create("/api/v1/orders/" + orderId.value()))
|
|
627
|
+
.body(order);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
test: |
|
|
632
|
+
// Unit Test with Arrange-Act-Assert pattern
|
|
633
|
+
@ExtendWith(MockitoExtension.class)
|
|
634
|
+
class CreateOrderUseCaseTest {
|
|
635
|
+
@Mock
|
|
636
|
+
private OrderRepository orderRepository;
|
|
637
|
+
@Mock
|
|
638
|
+
private CustomerRepository customerRepository;
|
|
639
|
+
@Mock
|
|
640
|
+
private DomainEventPublisher eventPublisher;
|
|
641
|
+
|
|
642
|
+
@InjectMocks
|
|
643
|
+
private CreateOrderUseCase useCase;
|
|
644
|
+
|
|
645
|
+
@Test
|
|
646
|
+
void should_CreateOrder_When_CustomerExistsAndItemsValid() {
|
|
647
|
+
// Arrange
|
|
648
|
+
var customerId = new CustomerId(UUID.randomUUID());
|
|
649
|
+
var customer = new Customer(customerId, "John Doe");
|
|
650
|
+
var command = new CreateOrderCommand(customerId, List.of(
|
|
651
|
+
new OrderItem(ProductId.of("PROD-1"), Quantity.of(2))
|
|
652
|
+
));
|
|
653
|
+
|
|
654
|
+
when(customerRepository.findById(customerId)).thenReturn(Optional.of(customer));
|
|
655
|
+
when(orderRepository.save(any())).thenAnswer(i -> i.getArgument(0));
|
|
656
|
+
|
|
657
|
+
// Act
|
|
658
|
+
var orderId = useCase.execute(command);
|
|
659
|
+
|
|
660
|
+
// Assert
|
|
661
|
+
assertThat(orderId).isNotNull();
|
|
662
|
+
verify(orderRepository).save(argThat(order ->
|
|
663
|
+
order.getCustomerId().equals(customerId) &&
|
|
664
|
+
order.getLines().size() == 1
|
|
665
|
+
));
|
|
666
|
+
verify(eventPublisher).publishAll(anyList());
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
@Test
|
|
670
|
+
void should_ThrowException_When_CustomerNotFound() {
|
|
671
|
+
// Arrange
|
|
672
|
+
var command = new CreateOrderCommand(
|
|
673
|
+
new CustomerId(UUID.randomUUID()),
|
|
674
|
+
List.of()
|
|
675
|
+
);
|
|
676
|
+
when(customerRepository.findById(any())).thenReturn(Optional.empty());
|
|
677
|
+
|
|
678
|
+
// Act & Assert
|
|
679
|
+
assertThatThrownBy(() -> useCase.execute(command))
|
|
680
|
+
.isInstanceOf(EntityNotFoundException.class)
|
|
681
|
+
.hasMessageContaining("Customer");
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
repository: |
|
|
686
|
+
// Repository Interface (Domain Layer - Port)
|
|
687
|
+
public interface OrderRepository {
|
|
688
|
+
Optional<Order> findById(OrderId id);
|
|
689
|
+
Order save(Order order);
|
|
690
|
+
List<Order> findByCustomerId(CustomerId customerId);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Repository Implementation (Infrastructure Layer - Adapter)
|
|
694
|
+
@Repository
|
|
695
|
+
@RequiredArgsConstructor
|
|
696
|
+
class JpaOrderRepository implements OrderRepository {
|
|
697
|
+
private final OrderJpaRepository jpaRepository;
|
|
698
|
+
private final OrderMapper mapper;
|
|
699
|
+
|
|
700
|
+
@Override
|
|
701
|
+
public Optional<Order> findById(OrderId id) {
|
|
702
|
+
return jpaRepository.findById(id.value())
|
|
703
|
+
.map(mapper::toDomain);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
@Override
|
|
707
|
+
public Order save(Order order) {
|
|
708
|
+
var entity = mapper.toEntity(order);
|
|
709
|
+
var saved = jpaRepository.save(entity);
|
|
710
|
+
return mapper.toDomain(saved);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
# ----------------------------------------------------------------------------
|
|
715
|
+
# ANTI-PATTERNS - What NOT to do
|
|
716
|
+
# ----------------------------------------------------------------------------
|
|
717
|
+
antiPatterns:
|
|
718
|
+
anemicDomainModel:
|
|
719
|
+
description: "Domain objects with only getters/setters, no business logic"
|
|
720
|
+
example: |
|
|
721
|
+
// BAD: Anemic domain model
|
|
722
|
+
public class Order {
|
|
723
|
+
private OrderStatus status;
|
|
724
|
+
public OrderStatus getStatus() { return status; }
|
|
725
|
+
public void setStatus(OrderStatus status) { this.status = status; }
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Service does all the work
|
|
729
|
+
public class OrderService {
|
|
730
|
+
public void confirmOrder(Order order) {
|
|
731
|
+
if (order.getLines().isEmpty()) throw new Exception();
|
|
732
|
+
order.setStatus(OrderStatus.CONFIRMED); // Logic outside domain!
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
fix: "Move business logic INTO the domain entity"
|
|
736
|
+
|
|
737
|
+
primitiveObsession:
|
|
738
|
+
description: "Using primitives instead of Value Objects"
|
|
739
|
+
example: |
|
|
740
|
+
// BAD: Using primitives
|
|
741
|
+
public class Order {
|
|
742
|
+
private String customerId; // Just a String!
|
|
743
|
+
private double amount; // Precision issues!
|
|
744
|
+
private String currency; // No validation!
|
|
745
|
+
}
|
|
746
|
+
fix: "Use Value Objects: CustomerId, Money(amount, currency)"
|
|
747
|
+
|
|
748
|
+
godClass:
|
|
749
|
+
description: "Class with too many responsibilities"
|
|
750
|
+
example: |
|
|
751
|
+
// BAD: God class doing everything
|
|
752
|
+
public class OrderService {
|
|
753
|
+
public Order createOrder() { ... }
|
|
754
|
+
public void sendEmail() { ... }
|
|
755
|
+
public void generatePdf() { ... }
|
|
756
|
+
public void processPayment() { ... }
|
|
757
|
+
public void updateInventory() { ... }
|
|
758
|
+
public void notifyWarehouse() { ... }
|
|
759
|
+
}
|
|
760
|
+
fix: "Split into focused services: OrderService, NotificationService, PaymentService, etc."
|
|
761
|
+
|
|
762
|
+
fieldInjection:
|
|
763
|
+
description: "Using @Autowired on fields instead of constructor"
|
|
764
|
+
example: |
|
|
765
|
+
// BAD: Field injection
|
|
766
|
+
@Service
|
|
767
|
+
public class OrderService {
|
|
768
|
+
@Autowired
|
|
769
|
+
private OrderRepository repository; // Hard to test!
|
|
770
|
+
|
|
771
|
+
@Autowired
|
|
772
|
+
private PaymentService paymentService;
|
|
773
|
+
}
|
|
774
|
+
fix: |
|
|
775
|
+
// GOOD: Constructor injection
|
|
776
|
+
@Service
|
|
777
|
+
@RequiredArgsConstructor
|
|
778
|
+
public class OrderService {
|
|
779
|
+
private final OrderRepository repository;
|
|
780
|
+
private final PaymentService paymentService;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
catchingGenericException:
|
|
784
|
+
description: "Catching Exception instead of specific types"
|
|
785
|
+
example: |
|
|
786
|
+
// BAD: Catching generic Exception
|
|
787
|
+
try {
|
|
788
|
+
processOrder(order);
|
|
789
|
+
} catch (Exception e) { // Catches EVERYTHING including NPE, OOM
|
|
790
|
+
log.error("Error", e);
|
|
791
|
+
}
|
|
792
|
+
fix: |
|
|
793
|
+
// GOOD: Catch specific exceptions
|
|
794
|
+
try {
|
|
795
|
+
processOrder(order);
|
|
796
|
+
} catch (OrderNotFoundException e) {
|
|
797
|
+
throw new ResponseStatusException(NOT_FOUND, e.getMessage());
|
|
798
|
+
} catch (InsufficientInventoryException e) {
|
|
799
|
+
throw new ResponseStatusException(CONFLICT, e.getMessage());
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
businessLogicInController:
|
|
803
|
+
description: "Putting business logic in REST controllers"
|
|
804
|
+
example: |
|
|
805
|
+
// BAD: Logic in controller
|
|
806
|
+
@PostMapping("/orders/{id}/confirm")
|
|
807
|
+
public void confirm(@PathVariable Long id) {
|
|
808
|
+
Order order = repository.findById(id);
|
|
809
|
+
if (order.getLines().isEmpty()) {
|
|
810
|
+
throw new BadRequestException("Empty order");
|
|
811
|
+
}
|
|
812
|
+
order.setStatus(CONFIRMED);
|
|
813
|
+
order.setConfirmedAt(Instant.now());
|
|
814
|
+
repository.save(order);
|
|
815
|
+
emailService.sendConfirmation(order);
|
|
816
|
+
}
|
|
817
|
+
fix: "Controllers should only delegate to use cases/services"
|
|
818
|
+
|
|
819
|
+
nPlusOneQueries:
|
|
820
|
+
description: "Loading related entities in a loop"
|
|
821
|
+
example: |
|
|
822
|
+
// BAD: N+1 queries
|
|
823
|
+
List<Order> orders = orderRepository.findAll();
|
|
824
|
+
for (Order order : orders) {
|
|
825
|
+
List<OrderLine> lines = lineRepository.findByOrderId(order.getId());
|
|
826
|
+
// This executes N additional queries!
|
|
827
|
+
}
|
|
828
|
+
fix: "Use JOIN FETCH or @EntityGraph to load in single query"
|
|
829
|
+
|
|
830
|
+
returningEntitiesFromControllers:
|
|
831
|
+
description: "Exposing JPA entities in API responses"
|
|
832
|
+
example: |
|
|
833
|
+
// BAD: Returning entity
|
|
834
|
+
@GetMapping("/orders/{id}")
|
|
835
|
+
public OrderEntity getOrder(@PathVariable Long id) {
|
|
836
|
+
return orderRepository.findById(id); // Exposes internal structure!
|
|
837
|
+
}
|
|
838
|
+
fix: "Use DTOs/Response objects to control API shape"
|